Compare commits

..

12 Commits

Author SHA1 Message Date
Hanefi Onaldi fcd3b6c12f Changelog entries for 11.3.0 (#6856)
In this release, I tried something different. I experimented with adding
the PR number and title to the changelog right before each changelog
entry. This way, it is easier to track where a particular changelog
entry comes from. After reviews are over, I plan to remove those lines
with PR numbers and titles.

I went through all the PRs that are merged after 11.2.0 release and came
up with a list of PRs that may need help with changelog entries. You can
see details on PRs grouped in several sections below.

The following PRs below do not have a changelog entry. If you think that
this is a mistake, please share it in this PR along with a suggestion on
what the changelog item should be.

PR #6846 : fix 3 flaky tests in failure schedule
PR #6844 : Add CPU usage to citus_stat_tenants
PR #6833 : Fix citus_stat_tenants period updating bug
PR #6787 : Add more tests for ddl coverage
PR #6842 : Add build-cdc-* temporary directories to .gitignore
PR #6841 : Add build-cdc-* temporary directories to .gitignore
PR #6840 : Bump Citus to 12.0devel
PR #6824 : Fixes flakiness in multi_metadata_sync test
PR #6811 : Backport identity column improvements to v11.2
PR #6830 : In run_test.py actually return worker_count
PR #6825 : Fixes flakiness in multi_cluster_management test
PR #6816 : Refactor run_test.py
PR #6817 : Explicitly disallow local rels when inserting into dist table
PR #6821 : Rename citus stats tenants
PR #6822 : Add some more tests for initial sql support
PR #6819 : Fix flakyness in
citus_split_shard_by_split_points_deferred_drop
PR #6814 : Make python-regress based tests runnable with run_test.py
PR #6813 : Fix flaky multi_mx_schema_support test
PR #6720 : Convert columnar tap tests to pytest
PR #6812 : Revoke statistics permissions from public and grant them to
pg_monitor
PR #6769 : Citus stats tenants guc
PR #6807 : Fix the incorrect (constant) value passed to pointer-to-bool
parameter, pass a NULL as the value is not used
PR #6797 : Attribute local queries and cached plans on local execution
PR #6796 : Parse the annotation string correctly
PR #6762 : Add logs to citus_stats_tenants
PR #6773 : Add initial sql support for distributed tables that don't
have a shard key
PR #6792 : Disentangle MERGE planning code from the modify-planning code
path
PR #6761 : Citus stats tenants collector view
PR #6791 : Make 8 more tests runnable multiple times via run_test.py
PR #6786 : Refactor some of the planning code to accommodate a new
planning path for MERGE SQL
PR #6789 : Rename AllRelations.. functions to AllDistributedRelations..
PR #6788 : Actually skip arbitrary_configs_router & nested_execution for
AllNullDistKeyDefaultConfig
PR #6783 : Add a config for arbitrary config tests where all the tables
are null-shard-key tables
PR #6784 : Fix attach partition: citus local to null distributed
PR #6782 : Add an arbitrary config test heavily based on
multi_router_planner_fast_path.sql
PR #6781 : Decide what to do with router planner error at one place
PR #6778 : Support partitioning for dist tables with null dist keys
PR #6766 : fix pip lock file
PR #6764 : Make workerCount configurable for regression tests
PR #6745 : Add support for creating distributed tables with a null shard
key
PR #6696 : This implements MERGE phase-III
PR #6767 : Add pytest depedencies to Pipfile
PR #6760 : Decide core distribution params in CreateCitusTable
PR #6759 : Add multi_create_fdw into minimal_schedule
PR #6743 : Replace CITUS_TABLE_WITH_NO_DIST_KEY checks with
HasDistributionKey()
PR #6751 : Stabilize single_node.sql and others that report illegal node
removal
PR #6742 : Refactor CreateDistributedTable()
PR #6747 : Remove unused lock functions
PR #6744 : Fix multiple output version arbitrary config tests
PR #6741 : Stabilize single node tests
PR #6740 : Fix string eval bug in migration files check
PR #6736 : Make run_test.py and create_test.py importable without errors
PR #6734 : Don't blanket ignore flake8 E402 error
PR #6737 : Fixes bookworm packaging pipeline problem
PR #6735 : Fix run_test.py on python 3.9
PR #6733 : MERGE: In deparser, add missing check for RETURNING clause.
PR #6714 : Remove auto_explain workaround in citus explain hook for
ALTER TABLE
PR #6719 : Fix flaky test
PR #6718 : Add more powerfull dependency tracking to run_test.py
PR #6710 : Install non-vulnerable cryptography package
PR #6711 : Support compilation and run tests on latest PG versions
PR #6700 : Add auto-formatting and linting to our python code
PR #6707 : Allow multi_insert_select to run repeatably
PR #6708 : Fix flakyness in failure_create_distributed_table_non_empty
PR #6698 : Miscellaneous cleanup
PR #6704 : Update README for 11.2
PR #6703 : Fix dubious ownership error from git
PR #6690 : Bump Citus to 11.3devel

The following PRs have changelog entries that are too long to fit in a
single line. I'd expect authors to supply at changelog entries in
`DESCRIPTION:` lines that are at most 78 characters. If you want to
supply multi-line changelog items, you can have multiple lines that
start with `DESCRIPTION:` instead.

PR #6837 : fixes update propagation bug when
`citus_set_coordinator_host` is called more than once
PR #6738 :  Identity column implementation refactorings
PR #6756 : Schedule parallel shard moves in background rebalancer by
removing task dependencies between shard moves across colocation groups.
PR #6793 : Add a GUC to disallow planning the queries that reference
non-colocated tables via router planner
PR #6726 : fix memory leak during altering distributed table with a lot
of partition and shards
PR #6722 : fix memory leak during distribution of a table with a lot of
partitions
PR #6693 : prevent memory leak during ConvertTable with a lot of
partitions

The following PR had an empty `DESCRIPTION:` line. This generates an
empty changelog line that needs to be removed manually. Please either
provide a short entry, or remove `DESCRIPTION:` line completely.

PR #6810 : Make CDC decoder an independent extension
PR #6827 : Makefile changes to build CDC in builddir for pgoutput and
wal2json.

---------

Co-authored-by: Onur Tirtir <onurcantirtir@gmail.com>
(cherry picked from commit 934430003e)
2023-05-02 12:39:15 +03:00
Hanefi Onaldi 6d833a90e5 Bump columnar to 11.3
(cherry picked from commit eca70b29a67ed0b004c820bcb9f716ee2b6013eb)
2023-05-02 12:39:15 +03:00
Ahmet Gedemenli 12c27ace2f Ignore nodes not allowed for shards, when planning rebalance steps (#6887)
We are handling colocation groups with shard group count less than the
worker node count, using a method different than the usual rebalancer.
See #6739
While making the decision of using this method or not, we should've
ignored the nodes that are marked `shouldhaveshards = false`. This PR
excludes those nodes when making the decision.

Adds a test such that:
 coordinator: []
 worker 1: [1_1, 1_2]
 worker 2: [2_1, 2_2]
(rebalance)
 coordinator: []
 worker 1: [1_1, 2_1]
 worker 2: [1_2, 2_2]

If we take the coordinator into account, the rebalancer considers the
first state as balanced and does nothing (because shard_count <
worker_count)
But with this pr, we ignore the coordinator because it's
shouldhaveshards = false
So the rebalancer distributes each colocation group to both workers

Also, fixes an unrelated flaky test in the same file

(cherry picked from commit 59ccf364df)
2023-05-01 12:55:08 +02:00
aykut-bozkurt 262c335860 break sequence dependency during table creation (#6889)
We need to break sequence dependency for a table while creating the
table during non-transactional metadata sync to ensure idempotency of
the creation of the table.

**Problem:**
When we send `SELECT
pg_catalog.worker_drop_sequence_dependency(logicalrelid::regclass::text)
FROM pg_dist_partition` to workers during the non-transactional sync,
table might not be in `pg_dist_partition` at worker, and sequence
dependency is not broken at the worker.

**Solution:**
We break sequence dependency via `SELECT
pg_catalog.worker_drop_sequence_dependency(logicalrelid::regclass::text)`
for each table while creating it at the workers. It is safe to send
since the udf is a no-op when there is no sequence dependency.

DESCRIPTION: Fixes a bug related to sequence idempotency at
non-transactional sync.

Fixes https://github.com/citusdata/citus/issues/6888.

(cherry picked from commit 8cb69cfd13)
2023-04-28 15:12:02 +03:00
Emel Şimşek 7b98fbb05e When creating a HTAB we need to use HASH_COMPARE flag in order to set a user defined comparison function. (#6845)
DESCRIPTION: Fixes memory errors, caught by valgrind, of type
"conditional jump or move depends on uninitialized value"

When running Citus tests under Postgres with valgrind, the test cases
calling into `NonBlockingShardSplit` function produce valgrind errors of
type "conditional jump or move depends on uninitialized value".

The issue is caused by creating a HTAB in a wrong way. HASH_COMPARE flag
should have been used when creating a HTAB with user defined comparison
function. In the absence of HASH_COMPARE flag, HTAB falls back into
built-in string comparison function. However, valgrind somehow discovers
that the match function is not assigned to the user defined function as
intended.

Fixes #6835

(cherry picked from commit e7a25d82c9)
2023-04-18 14:50:09 +03:00
Gokhan Gulbiz 58155c5779
Backport to 11.3 - Ensure partitionKeyValue and colocationId are set for proper tenant stats gathering (#6834) (#6862)
This PR updates the tenant stats implementation to set partitionKeyValue
and colocationId in ExecuteLocalTaskListExtended, in addition to
LocallyExecuteTaskPlan. This ensures that tenant stats can be properly
gathered regardless of the code path taken. The changes were initially
made while testing stored procedure calls for tenant stats.

(cherry picked from commit 8782ea1582)
2023-04-17 15:24:29 +03:00
aykut-bozkurt 17149b92b2 fix 3 flaky tests in failure schedule (#6846)
Fixed 3 flaky tests in failure tests which caused flakiness in other
tests due to changed node and group sequence ids during node
addition-removal.

(cherry picked from commit 3286ec59e9)
2023-04-13 13:19:35 +03:00
aykut-bozkurt 1a9066c34a fixes update propagation bug when `citus_set_coordinator_host` is called more than once (#6837)
DESCRIPTION: Fixes update propagation bug when
`citus_set_coordinator_host` is called more than once.

Fixes https://github.com/citusdata/citus/issues/6731.

(cherry picked from commit a20f7e1a55)
2023-04-13 13:18:28 +03:00
Halil Ozan Akgül e14f4c3dee Add CPU usage to citus_stat_tenants (#6844)
This PR adds CPU usage to `citus_stat_tenants` monitor.
CPU usage is tracked in periods, similar to query counts.

(cherry picked from commit 9ba70696f7)
2023-04-12 17:46:00 +03:00
Halil Ozan Akgül 5525676aad Fix citus_stat_tenants period updating bug (#6833)
Fixes the bug that causes updating the citus_stat_tenants periods
incorrectly.

`TimestampDifferenceExceeds` expects the difference in milliseconds but
it was microseconds, this is fixed.
`tenantStats->lastQueryTime` was updated during monitoring too, now it's
updated only when there are tenant queries.

(cherry picked from commit 8b50e95dc8)
2023-04-12 17:45:44 +03:00
rajeshkt78 234df62106
Add build-cdc-* temporary directories to .gitignore (#6842)
The CDC decoder buillds different versions of CDC base decoders during
the build. Since the source files are copied to the temporay
directories, they come in git status for files to be added. So these
directories and a temporary CDC TAP test directory(tmpcheck) are added
to .gitignore file.
2023-04-11 08:54:43 +05:30
Onur Tirtir 5dd08835df Bump citus version to 11.3.0 2023-04-10 11:16:58 +03:00
1290 changed files with 37421 additions and 112827 deletions

1035
.circleci/config.yml Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,33 +0,0 @@
# gdbpg.py contains scripts to nicely print the postgres datastructures
# while in a gdb session. Since the vscode debugger is based on gdb this
# actually also works when debugging with vscode. Providing nice tools
# to understand the internal datastructures we are working with.
source /root/gdbpg.py
# when debugging postgres it is convenient to _always_ have a breakpoint
# trigger when an error is logged. Because .gdbinit is sourced before gdb
# is fully attached and has the sources loaded. To make sure the breakpoint
# is added when the library is loaded we temporary set the breakpoint pending
# to on. After we have added out breakpoint we revert back to the default
# configuration for breakpoint pending.
# The breakpoint is hard to read, but at entry of the function we don't have
# the level loaded in elevel. Instead we hardcode the location where the
# level of the current error is stored. Also gdb doesn't understand the
# ERROR symbol so we hardcode this to the value of ERROR. It is very unlikely
# this value will ever change in postgres, but if it does we might need to
# find a way to conditionally load the correct breakpoint.
set breakpoint pending on
break elog.c:errfinish if errordata[errordata_stack_depth].elevel == 21
set breakpoint pending auto
echo \n
echo ----------------------------------------------------------------------------------\n
echo when attaching to a postgres backend a breakpoint will be set on elog.c:errfinish \n
echo it will only break on errors being raised in postgres \n
echo \n
echo to disable this breakpoint from vscode run `-exec disable 1` in the debug console \n
echo this assumes it's the first breakpoint loaded as it is loaded from .gdbinit \n
echo this can be verified with `-exec info break`, enabling can be done with \n
echo `-exec enable 1` \n
echo ----------------------------------------------------------------------------------\n
echo \n

View File

@ -1 +0,0 @@
postgresql-*.tar.bz2

View File

@ -1,7 +0,0 @@
\timing on
\pset linestyle unicode
\pset border 2
\setenv PAGER 'pspg --no-mouse -bX --no-commandbar --no-topbar'
\set HISTSIZE 100000
\set PROMPT1 '\n%[%033[1m%]%M %n@%/:%> (PID: %p)%R%[%033[0m%]%# '
\set PROMPT2 ' '

View File

@ -1,12 +0,0 @@
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
[packages]
docopt = "*"
[dev-packages]
[requires]
python_version = "3.9"

28
.devcontainer/.vscode/Pipfile.lock generated vendored
View File

@ -1,28 +0,0 @@
{
"_meta": {
"hash": {
"sha256": "6956a6700ead5804aa56bd597c93bb4a13f208d2d49d3b5399365fd240ca0797"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.9"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
"default": {
"docopt": {
"hashes": [
"sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491"
],
"index": "pypi",
"version": "==0.6.2"
}
},
"develop": {}
}

View File

@ -1,84 +0,0 @@
#! /usr/bin/env pipenv-shebang
"""Generate C/C++ properties file for VSCode.
Uses pgenv to iterate postgres versions and generate
a C/C++ properties file for VSCode containing the
include paths for the postgres headers.
Usage:
generate_c_cpp_properties-json.py <target_path>
generate_c_cpp_properties-json.py (-h | --help)
generate_c_cpp_properties-json.py --version
Options:
-h --help Show this screen.
--version Show version.
"""
import json
import subprocess
from docopt import docopt
def main(args):
target_path = args['<target_path>']
output = subprocess.check_output(['pgenv', 'versions'])
# typical output is:
# 14.8 pgsql-14.8
# * 15.3 pgsql-15.3
# 16beta2 pgsql-16beta2
# where the line marked with a * is the currently active version
#
# we are only interested in the first word of each line, which is the version number
# thus we strip the whitespace and the * from the line and split it into words
# and take the first word
versions = [line.strip('* ').split()[0] for line in output.decode('utf-8').splitlines()]
# create the list of configurations per version
configurations = []
for version in versions:
configurations.append(generate_configuration(version))
# create the json file
c_cpp_properties = {
"configurations": configurations,
"version": 4
}
# write the c_cpp_properties.json file
with open(target_path, 'w') as f:
json.dump(c_cpp_properties, f, indent=4)
def generate_configuration(version):
"""Returns a configuration for the given postgres version.
>>> generate_configuration('14.8')
{
"name": "Citus Development Configuration - Postgres 14.8",
"includePath": [
"/usr/local/include",
"/home/citus/.pgenv/src/postgresql-14.8/src/**",
"${workspaceFolder}/**",
"${workspaceFolder}/src/include/",
],
"configurationProvider": "ms-vscode.makefile-tools"
}
"""
return {
"name": f"Citus Development Configuration - Postgres {version}",
"includePath": [
"/usr/local/include",
f"/home/citus/.pgenv/src/postgresql-{version}/src/**",
"${workspaceFolder}/**",
"${workspaceFolder}/src/include/",
],
"configurationProvider": "ms-vscode.makefile-tools"
}
if __name__ == '__main__':
arguments = docopt(__doc__, version='0.1.0')
main(arguments)

View File

@ -1,40 +0,0 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Attach Citus (devcontainer)",
"type": "cppdbg",
"request": "attach",
"processId": "${command:pickProcess}",
"program": "/home/citus/.pgenv/pgsql/bin/postgres",
"additionalSOLibSearchPath": "/home/citus/.pgenv/pgsql/lib",
"setupCommands": [
{
"text": "handle SIGUSR1 noprint nostop pass",
"description": "let gdb not stop when SIGUSR1 is sent to process",
"ignoreFailures": true
}
],
},
{
"name": "Open core file",
"type": "cppdbg",
"request": "launch",
"program": "/home/citus/.pgenv/pgsql/bin/postgres",
"coreDumpPath": "${input:corefile}",
"cwd": "${workspaceFolder}",
"MIMode": "gdb",
}
],
"inputs": [
{
"id": "corefile",
"type": "command",
"command": "extension.commandvariable.file.pickFile",
"args": {
"dialogTitle": "Select core file",
"include": "**/core*",
},
},
],
}

View File

@ -1,222 +0,0 @@
FROM ubuntu:22.04 AS base
# environment is to make python pass an interactive shell, probably not the best timezone given a wide variety of colleagues
ENV TZ=UTC
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# install build tools
RUN apt update && apt install -y \
bison \
bzip2 \
cpanminus \
curl \
docbook-xml \
docbook-xsl \
flex \
gcc \
git \
libcurl4-gnutls-dev \
libicu-dev \
libkrb5-dev \
liblz4-dev \
libpam0g-dev \
libreadline-dev \
libselinux1-dev \
libssl-dev \
libxml2-utils \
libxslt-dev \
libzstd-dev \
locales \
make \
perl \
pkg-config \
python3 \
python3-pip \
software-properties-common \
sudo \
uuid-dev \
valgrind \
xsltproc \
zlib1g-dev \
&& add-apt-repository ppa:deadsnakes/ppa -y \
&& apt install -y \
python3.9-full \
# software properties pulls in pkexec, which makes the debugger unusable in vscode
&& apt purge -y \
software-properties-common \
&& apt autoremove -y \
&& apt clean
RUN sudo pip3 install pipenv pipenv-shebang
RUN cpanm install IPC::Run
RUN locale-gen en_US.UTF-8
# add the citus user to sudoers and allow all sudoers to login without a password prompt
RUN useradd -ms /bin/bash citus \
&& usermod -aG sudo citus \
&& echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
WORKDIR /home/citus
USER citus
# run all make commands with the number of cores available
RUN echo "export MAKEFLAGS=\"-j \$(nproc)\"" >> "/home/citus/.bashrc"
RUN git clone --branch v1.3.2 --depth 1 https://github.com/theory/pgenv.git .pgenv
COPY --chown=citus:citus pgenv/config/ .pgenv/config/
ENV PATH="/home/citus/.pgenv/bin:${PATH}"
ENV PATH="/home/citus/.pgenv/pgsql/bin:${PATH}"
USER citus
# build postgres versions separately for effective parrallelism and caching of already built versions when changing only certain versions
FROM base AS pg15
RUN MAKEFLAGS="-j $(nproc)" pgenv build 15.13
RUN rm .pgenv/src/*.tar*
RUN make -C .pgenv/src/postgresql-*/ clean
RUN make -C .pgenv/src/postgresql-*/src/include install
# create a staging directory with all files we want to copy from our pgenv build
# we will copy the contents of the staged folder into the final image at once
RUN mkdir .pgenv-staging/
RUN cp -r .pgenv/src .pgenv/pgsql-* .pgenv/config .pgenv-staging/
RUN rm .pgenv-staging/config/default.conf
FROM base AS pg16
RUN MAKEFLAGS="-j $(nproc)" pgenv build 16.9
RUN rm .pgenv/src/*.tar*
RUN make -C .pgenv/src/postgresql-*/ clean
RUN make -C .pgenv/src/postgresql-*/src/include install
# create a staging directory with all files we want to copy from our pgenv build
# we will copy the contents of the staged folder into the final image at once
RUN mkdir .pgenv-staging/
RUN cp -r .pgenv/src .pgenv/pgsql-* .pgenv/config .pgenv-staging/
RUN rm .pgenv-staging/config/default.conf
FROM base AS pg17
RUN MAKEFLAGS="-j $(nproc)" pgenv build 17.5
RUN rm .pgenv/src/*.tar*
RUN make -C .pgenv/src/postgresql-*/ clean
RUN make -C .pgenv/src/postgresql-*/src/include install
# create a staging directory with all files we want to copy from our pgenv build
# we will copy the contents of the staged folder into the final image at once
RUN mkdir .pgenv-staging/
RUN cp -r .pgenv/src .pgenv/pgsql-* .pgenv/config .pgenv-staging/
RUN rm .pgenv-staging/config/default.conf
FROM base AS uncrustify-builder
RUN sudo apt update && sudo apt install -y cmake tree
WORKDIR /uncrustify
RUN curl -L https://github.com/uncrustify/uncrustify/archive/uncrustify-0.68.1.tar.gz | tar xz
WORKDIR /uncrustify/uncrustify-uncrustify-0.68.1/
RUN mkdir build
WORKDIR /uncrustify/uncrustify-uncrustify-0.68.1/build/
RUN cmake ..
RUN MAKEFLAGS="-j $(nproc)" make -s
RUN make install DESTDIR=/uncrustify
# builder for all pipenv's to get them contained in a single layer
FROM base AS pipenv
WORKDIR /workspaces/citus/
# tools to sync pgenv with vscode
COPY --chown=citus:citus .vscode/Pipfile .vscode/Pipfile.lock .devcontainer/.vscode/
RUN ( cd .devcontainer/.vscode && pipenv install )
# environment to run our failure tests
COPY --chown=citus:citus src/ src/
RUN ( cd src/test/regress && pipenv install )
# assemble the final container by copying over the artifacts from separately build containers
FROM base AS devcontainer
LABEL org.opencontainers.image.source=https://github.com/citusdata/citus
LABEL org.opencontainers.image.description="Development container for the Citus project"
LABEL org.opencontainers.image.licenses=AGPL-3.0-only
RUN yes | sudo unminimize
# install developer productivity tools
RUN sudo apt update \
&& sudo apt install -y \
autoconf2.69 \
bash-completion \
fswatch \
gdb \
htop \
libdbd-pg-perl \
libdbi-perl \
lsof \
man \
net-tools \
psmisc \
pspg \
tree \
vim \
&& sudo apt clean
# Since gdb will run in the context of the root user when debugging citus we will need to both
# download the gdbpg.py script as the root user, into their home directory, as well as add .gdbinit
# as a file owned by root
# This will make that as soon as the debugger attaches to a postgres backend (or frankly any other process)
# the gdbpg.py script will be sourced and the developer can direcly use it.
RUN sudo curl -o /root/gdbpg.py https://raw.githubusercontent.com/tvesely/gdbpg/6065eee7872457785f830925eac665aa535caf62/gdbpg.py
COPY --chown=root:root .gdbinit /root/
# install developer dependencies in the global environment
RUN --mount=type=bind,source=requirements.txt,target=requirements.txt pip install -r requirements.txt
# for persistent bash history across devcontainers we need to have
# a) a directory to store the history in
# b) a prompt command to append the history to the file
# c) specify the history file to store the history in
# b and c are done in the .bashrc to make it persistent across shells only
RUN sudo install -d -o citus -g citus /commandhistory \
&& echo "export PROMPT_COMMAND='history -a' && export HISTFILE=/commandhistory/.bash_history" >> "/home/citus/.bashrc"
# install citus-dev
RUN git clone --branch develop https://github.com/citusdata/tools.git citus-tools \
&& ( cd citus-tools/citus_dev && pipenv install ) \
&& mkdir -p ~/.local/bin \
&& ln -s /home/citus/citus-tools/citus_dev/citus_dev-pipenv .local/bin/citus_dev \
&& sudo make -C citus-tools/uncrustify install bindir=/usr/local/bin pkgsysconfdir=/usr/local/etc/ \
&& mkdir -p ~/.local/share/bash-completion/completions/ \
&& ln -s ~/citus-tools/citus_dev/bash_completion ~/.local/share/bash-completion/completions/citus_dev
# TODO some LC_ALL errors, possibly solved by locale-gen
RUN git clone https://github.com/so-fancy/diff-so-fancy.git \
&& mkdir -p ~/.local/bin \
&& ln -s /home/citus/diff-so-fancy/diff-so-fancy .local/bin/
COPY --link --from=uncrustify-builder /uncrustify/usr/ /usr/
COPY --link --from=pg15 /home/citus/.pgenv-staging/ /home/citus/.pgenv/
COPY --link --from=pg16 /home/citus/.pgenv-staging/ /home/citus/.pgenv/
COPY --link --from=pg17 /home/citus/.pgenv-staging/ /home/citus/.pgenv/
COPY --link --from=pipenv /home/citus/.local/share/virtualenvs/ /home/citus/.local/share/virtualenvs/
# place to run your cluster with citus_dev
VOLUME /data
RUN sudo mkdir /data \
&& sudo chown citus:citus /data
COPY --chown=citus:citus .psqlrc .
# with the copy linking of layers github actions seem to misbehave with the ownership of the
# directories leading upto the link, hence a small patch layer to have to right ownerships set
RUN sudo chown --from=root:root citus:citus -R ~
# sets default pg version
RUN pgenv switch 17.5
# make connecting to the coordinator easy
ENV PGPORT=9700

View File

@ -1,11 +0,0 @@
init: ../.vscode/c_cpp_properties.json ../.vscode/launch.json
../.vscode:
mkdir -p ../.vscode
../.vscode/launch.json: ../.vscode .vscode/launch.json
cp .vscode/launch.json ../.vscode/launch.json
../.vscode/c_cpp_properties.json: ../.vscode
./.vscode/generate_c_cpp_properties-json.py ../.vscode/c_cpp_properties.json

View File

@ -1,37 +0,0 @@
{
"image": "ghcr.io/citusdata/citus-devcontainer:main",
"runArgs": [
"--cap-add=SYS_PTRACE",
"--ulimit=core=-1",
],
"forwardPorts": [
9700
],
"customizations": {
"vscode": {
"extensions": [
"eamodio.gitlens",
"GitHub.copilot-chat",
"GitHub.copilot",
"github.vscode-github-actions",
"github.vscode-pull-request-github",
"ms-vscode.cpptools-extension-pack",
"ms-vsliveshare.vsliveshare",
"rioj7.command-variable",
],
"settings": {
"files.exclude": {
"**/*.o": true,
"**/.deps/": true,
}
},
}
},
"mounts": [
"type=volume,target=/data",
"source=citus-bashhistory,target=/commandhistory,type=volume",
],
"updateContentCommand": "./configure",
"postCreateCommand": "make -C .devcontainer/",
}

View File

@ -1,15 +0,0 @@
PGENV_MAKE_OPTIONS=(-s)
PGENV_CONFIGURE_OPTIONS=(
--enable-debug
--enable-depend
--enable-cassert
--enable-tap-tests
'CFLAGS=-ggdb -Og -g3 -fno-omit-frame-pointer -DUSE_VALGRIND'
--with-openssl
--with-libxml
--with-libxslt
--with-uuid=e2fs
--with-icu
--with-lz4
)

View File

@ -1,9 +0,0 @@
black==23.11.0
click==8.1.7
isort==5.12.0
mypy-extensions==1.0.0
packaging==23.2
pathspec==0.11.2
platformdirs==4.0.0
tomli==2.0.1
typing_extensions==4.8.0

View File

@ -1,28 +0,0 @@
[[source]]
name = "pypi"
url = "https://pypi.python.org/simple"
verify_ssl = true
[packages]
mitmproxy = {editable = true, ref = "main", git = "https://github.com/citusdata/mitmproxy.git"}
construct = "*"
docopt = "==0.6.2"
cryptography = ">=41.0.4"
pytest = "*"
psycopg = "*"
filelock = "*"
pytest-asyncio = "*"
pytest-timeout = "*"
pytest-xdist = "*"
pytest-repeat = "*"
pyyaml = "*"
werkzeug = "==2.3.7"
[dev-packages]
black = "*"
isort = "*"
flake8 = "*"
flake8-bugbear = "*"
[requires]
python_version = "3.9"

File diff suppressed because it is too large Load Diff

View File

@ -4,4 +4,3 @@ extend-ignore = E203
# black will truncate to 88 characters usually, but long string literals it
# might keep. That's fine in most cases unless it gets really excessive.
max-line-length = 150
exclude = .git,__pycache__,vendor,tmp_*

4
.gitattributes vendored
View File

@ -25,9 +25,9 @@ configure -whitespace
# except these exceptions...
src/backend/distributed/utils/citus_outfuncs.c -citus-style
src/backend/distributed/deparser/ruleutils_13.c -citus-style
src/backend/distributed/deparser/ruleutils_14.c -citus-style
src/backend/distributed/deparser/ruleutils_15.c -citus-style
src/backend/distributed/deparser/ruleutils_16.c -citus-style
src/backend/distributed/deparser/ruleutils_17.c -citus-style
src/backend/distributed/commands/index_pg_source.c -citus-style
src/include/distributed/citus_nodes.h -citus-style

View File

@ -1,23 +0,0 @@
name: 'Parallelization matrix'
inputs:
count:
required: false
default: 32
outputs:
json:
value: ${{ steps.generate_matrix.outputs.json }}
runs:
using: "composite"
steps:
- name: Generate parallelization matrix
id: generate_matrix
shell: bash
run: |-
json_array="{\"include\": ["
for ((i = 1; i <= ${{ inputs.count }}; i++)); do
json_array+="{\"id\":\"$i\"},"
done
json_array=${json_array%,}
json_array+=" ]}"
echo "json=$json_array" >> "$GITHUB_OUTPUT"
echo "json=$json_array"

View File

@ -1,38 +0,0 @@
name: save_logs_and_results
inputs:
folder:
required: false
default: "log"
runs:
using: composite
steps:
- uses: actions/upload-artifact@v4.6.0
name: Upload logs
with:
name: ${{ inputs.folder }}
if-no-files-found: ignore
path: |
src/test/**/proxy.output
src/test/**/results/
src/test/**/tmp_check/master/log
src/test/**/tmp_check/worker.57638/log
src/test/**/tmp_check/worker.57637/log
src/test/**/*.diffs
src/test/**/out/ddls.sql
src/test/**/out/queries.sql
src/test/**/logfile_*
/tmp/pg_upgrade_newData_logs
- name: Publish regression.diffs
run: |-
diffs="$(find src/test/regress -name "*.diffs" -exec cat {} \;)"
if ! [ -z "$diffs" ]; then
echo '```diff' >> $GITHUB_STEP_SUMMARY
echo -E "$diffs" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo -E $diffs
fi
shell: bash
- name: Print stack traces
run: "./ci/print_stack_trace.sh"
if: failure()
shell: bash

View File

@ -1,35 +0,0 @@
name: setup_extension
inputs:
pg_major:
required: false
skip_installation:
required: false
default: false
type: boolean
runs:
using: composite
steps:
- name: Expose $PG_MAJOR to Github Env
run: |-
if [ -z "${{ inputs.pg_major }}" ]; then
echo "PG_MAJOR=${PG_MAJOR}" >> $GITHUB_ENV
else
echo "PG_MAJOR=${{ inputs.pg_major }}" >> $GITHUB_ENV
fi
shell: bash
- uses: actions/download-artifact@v4.1.8
with:
name: build-${{ env.PG_MAJOR }}
- name: Install Extension
if: ${{ inputs.skip_installation == 'false' }}
run: tar xfv "install-$PG_MAJOR.tar" --directory /
shell: bash
- name: Configure
run: |-
chown -R circleci .
git config --global --add safe.directory ${GITHUB_WORKSPACE}
gosu circleci ./configure --without-pg-version-check
shell: bash
- name: Enable core dumps
run: ulimit -c unlimited
shell: bash

View File

@ -1,27 +0,0 @@
name: coverage
inputs:
flags:
required: false
codecov_token:
required: true
runs:
using: composite
steps:
- uses: codecov/codecov-action@v3
with:
flags: ${{ inputs.flags }}
token: ${{ inputs.codecov_token }}
verbose: true
gcov: true
- name: Create codeclimate coverage
run: |-
lcov --directory . --capture --output-file lcov.info
lcov --remove lcov.info -o lcov.info '/usr/*'
sed "s=^SF:$PWD/=SF:=g" -i lcov.info # relative pats are required by codeclimate
mkdir -p /tmp/codeclimate
cc-test-reporter format-coverage -t lcov -o /tmp/codeclimate/${{ inputs.flags }}.json lcov.info
shell: bash
- uses: actions/upload-artifact@v4.6.0
with:
path: "/tmp/codeclimate/*.json"
name: codeclimate-${{ inputs.flags }}

View File

@ -1,18 +1,3 @@
#!/bin/bash
set -ex
# Function to get the OS version
get_rpm_os_version() {
if [[ -f /etc/centos-release ]]; then
cat /etc/centos-release | awk '{print $4}'
elif [[ -f /etc/oracle-release ]]; then
cat /etc/oracle-release | awk '{print $5}'
else
echo "Unknown"
fi
}
package_type=${1}
# Since $HOME is set in GH_Actions as /github/home, pyenv fails to create virtualenvs.
@ -25,27 +10,11 @@ pyenv versions
pyenv virtualenv ${PACKAGING_PYTHON_VERSION} packaging_env
pyenv activate packaging_env
git clone -b v0.8.27 --depth=1 https://github.com/citusdata/tools.git tools
git clone -b v0.8.24 --depth=1 https://github.com/citusdata/tools.git tools
python3 -m pip install -r tools/packaging_automation/requirements.txt
echo "Package type: ${package_type}"
echo "OS version: $(get_rpm_os_version)"
# For RHEL 7, we need to install urllib3<2 due to below execution error
# ImportError: urllib3 v2.0 only supports OpenSSL 1.1.1+, currently the 'ssl'
# module is compiled with 'OpenSSL 1.0.2k-fips 26 Jan 2017'.
# See: https://github.com/urllib3/urllib3/issues/2168
if [[ ${package_type} == "rpm" && $(get_rpm_os_version) == 7* ]]; then
python3 -m pip uninstall -y urllib3
python3 -m pip install 'urllib3<2'
fi
python3 -m tools.packaging_automation.validate_build_output --output_file output.log \
--ignore_file .github/packaging/packaging_ignore.yml \
--package_type ${package_type}
pyenv deactivate
# Set $HOME back to /github/home
export HOME=${GITHUB_HOME}
# Print the output to the console

View File

@ -1,545 +0,0 @@
name: Build & Test
run-name: Build & Test - ${{ github.event.pull_request.title || github.ref_name }}
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
on:
workflow_dispatch:
inputs:
skip_test_flakyness:
required: false
default: false
type: boolean
push:
branches:
- "main"
- "release-*"
pull_request:
types: [opened, reopened,synchronize]
merge_group:
jobs:
# Since GHA does not interpolate env varibles in matrix context, we need to
# define them in a separate job and use them in other jobs.
params:
runs-on: ubuntu-latest
name: Initialize parameters
outputs:
build_image_name: "ghcr.io/citusdata/extbuilder"
test_image_name: "ghcr.io/citusdata/exttester"
citusupgrade_image_name: "ghcr.io/citusdata/citusupgradetester"
fail_test_image_name: "ghcr.io/citusdata/failtester"
pgupgrade_image_name: "ghcr.io/citusdata/pgupgradetester"
style_checker_image_name: "ghcr.io/citusdata/stylechecker"
style_checker_tools_version: "0.8.18"
sql_snapshot_pg_version: "17.5"
image_suffix: "-dev-d28f316"
pg15_version: '{ "major": "15", "full": "15.13" }'
pg16_version: '{ "major": "16", "full": "16.9" }'
pg17_version: '{ "major": "17", "full": "17.5" }'
upgrade_pg_versions: "15.13-16.9-17.5"
steps:
# Since GHA jobs need at least one step we use a noop step here.
- name: Set up parameters
run: echo 'noop'
check-sql-snapshots:
needs: params
runs-on: ubuntu-latest
container:
image: ${{ needs.params.outputs.build_image_name }}:${{ needs.params.outputs.sql_snapshot_pg_version }}${{ needs.params.outputs.image_suffix }}
options: --user root
steps:
- uses: actions/checkout@v4
- name: Check Snapshots
run: |
git config --global --add safe.directory ${GITHUB_WORKSPACE}
ci/check_sql_snapshots.sh
check-style:
needs: params
runs-on: ubuntu-latest
container:
image: ${{ needs.params.outputs.style_checker_image_name }}:${{ needs.params.outputs.style_checker_tools_version }}${{ needs.params.outputs.image_suffix }}
steps:
- name: Check Snapshots
run: |
git config --global --add safe.directory ${GITHUB_WORKSPACE}
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check C Style
run: citus_indent --check
- name: Check Python style
run: black --check .
- name: Check Python import order
run: isort --check .
- name: Check Python lints
run: flake8 .
- name: Fix whitespace
run: ci/editorconfig.sh && git diff --exit-code
- name: Remove useless declarations
run: ci/remove_useless_declarations.sh && git diff --cached --exit-code
- name: Sort and group includes
run: ci/sort_and_group_includes.sh && git diff --exit-code
- name: Normalize test output
run: ci/normalize_expected.sh && git diff --exit-code
- name: Check for C-style comments in migration files
run: ci/disallow_c_comments_in_migrations.sh && git diff --exit-code
- name: 'Check for comment--cached ns that start with # character in spec files'
run: ci/disallow_hash_comments_in_spec_files.sh && git diff --exit-code
- name: Check for gitignore entries .for source files
run: ci/fix_gitignore.sh && git diff --exit-code
- name: Check for lengths of changelog entries
run: ci/disallow_long_changelog_entries.sh
- name: Check for banned C API usage
run: ci/banned.h.sh
- name: Check for tests missing in schedules
run: ci/check_all_tests_are_run.sh
- name: Check if all CI scripts are actually run
run: ci/check_all_ci_scripts_are_run.sh
- name: Check if all GUCs are sorted alphabetically
run: ci/check_gucs_are_alphabetically_sorted.sh
- name: Check for missing downgrade scripts
run: ci/check_migration_files.sh
build:
needs: params
name: Build for PG${{ fromJson(matrix.pg_version).major }}
strategy:
fail-fast: false
matrix:
image_name:
- ${{ needs.params.outputs.build_image_name }}
image_suffix:
- ${{ needs.params.outputs.image_suffix}}
pg_version:
- ${{ needs.params.outputs.pg15_version }}
- ${{ needs.params.outputs.pg16_version }}
- ${{ needs.params.outputs.pg17_version }}
runs-on: ubuntu-latest
container:
image: "${{ matrix.image_name }}:${{ fromJson(matrix.pg_version).full }}${{ matrix.image_suffix }}"
options: --user root
steps:
- uses: actions/checkout@v4
- name: Expose $PG_MAJOR to Github Env
run: echo "PG_MAJOR=${PG_MAJOR}" >> $GITHUB_ENV
shell: bash
- name: Build
run: "./ci/build-citus.sh"
shell: bash
- uses: actions/upload-artifact@v4.6.0
with:
name: build-${{ env.PG_MAJOR }}
path: |-
./build-${{ env.PG_MAJOR }}/*
./install-${{ env.PG_MAJOR }}.tar
test-citus:
name: PG${{ fromJson(matrix.pg_version).major }} - ${{ matrix.make }}
strategy:
fail-fast: false
matrix:
suite:
- regress
image_name:
- ${{ needs.params.outputs.test_image_name }}
pg_version:
- ${{ needs.params.outputs.pg15_version }}
- ${{ needs.params.outputs.pg16_version }}
- ${{ needs.params.outputs.pg17_version }}
make:
- check-split
- check-multi
- check-multi-1
- check-multi-mx
- check-vanilla
- check-isolation
- check-operations
- check-follower-cluster
- 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
include:
- make: check-failure
pg_version: ${{ needs.params.outputs.pg15_version }}
suite: regress
image_name: ${{ needs.params.outputs.fail_test_image_name }}
- make: check-failure
pg_version: ${{ needs.params.outputs.pg16_version }}
suite: regress
image_name: ${{ needs.params.outputs.fail_test_image_name }}
- make: check-failure
pg_version: ${{ needs.params.outputs.pg17_version }}
suite: regress
image_name: ${{ needs.params.outputs.fail_test_image_name }}
- make: check-enterprise-failure
pg_version: ${{ needs.params.outputs.pg15_version }}
suite: regress
image_name: ${{ needs.params.outputs.fail_test_image_name }}
- make: check-enterprise-failure
pg_version: ${{ needs.params.outputs.pg16_version }}
suite: regress
image_name: ${{ needs.params.outputs.fail_test_image_name }}
- make: check-enterprise-failure
pg_version: ${{ needs.params.outputs.pg17_version }}
suite: regress
image_name: ${{ needs.params.outputs.fail_test_image_name }}
- make: check-pytest
pg_version: ${{ needs.params.outputs.pg15_version }}
suite: regress
image_name: ${{ needs.params.outputs.fail_test_image_name }}
- make: check-pytest
pg_version: ${{ needs.params.outputs.pg16_version }}
suite: regress
image_name: ${{ needs.params.outputs.fail_test_image_name }}
- make: check-pytest
pg_version: ${{ needs.params.outputs.pg17_version }}
suite: regress
image_name: ${{ needs.params.outputs.fail_test_image_name }}
- make: installcheck
suite: cdc
image_name: ${{ needs.params.outputs.test_image_name }}
pg_version: ${{ needs.params.outputs.pg15_version }}
- make: installcheck
suite: cdc
image_name: ${{ needs.params.outputs.test_image_name }}
pg_version: ${{ needs.params.outputs.pg16_version }}
- make: installcheck
suite: cdc
image_name: ${{ needs.params.outputs.test_image_name }}
pg_version: ${{ needs.params.outputs.pg17_version }}
- make: check-query-generator
pg_version: ${{ needs.params.outputs.pg15_version }}
suite: regress
image_name: ${{ needs.params.outputs.fail_test_image_name }}
- make: check-query-generator
pg_version: ${{ needs.params.outputs.pg16_version }}
suite: regress
image_name: ${{ needs.params.outputs.fail_test_image_name }}
- make: check-query-generator
pg_version: ${{ needs.params.outputs.pg17_version }}
suite: regress
image_name: ${{ needs.params.outputs.fail_test_image_name }}
runs-on: ubuntu-latest
container:
image: "${{ matrix.image_name }}:${{ fromJson(matrix.pg_version).full }}${{ needs.params.outputs.image_suffix }}"
options: --user root --dns=8.8.8.8
# Due to Github creates a default network for each job, we need to use
# --dns= to have similar DNS settings as our other CI systems or local
# machines. Otherwise, we may see different results.
needs:
- params
- build
steps:
- uses: actions/checkout@v4
- uses: "./.github/actions/setup_extension"
- name: Run Test
run: gosu circleci make -C src/test/${{ matrix.suite }} ${{ matrix.make }}
timeout-minutes: 20
- uses: "./.github/actions/save_logs_and_results"
if: always()
with:
folder: ${{ fromJson(matrix.pg_version).major }}_${{ matrix.make }}
- uses: "./.github/actions/upload_coverage"
if: always()
with:
flags: ${{ env.PG_MAJOR }}_${{ matrix.suite }}_${{ matrix.make }}
codecov_token: ${{ secrets.CODECOV_TOKEN }}
test-arbitrary-configs:
name: PG${{ fromJson(matrix.pg_version).major }} - check-arbitrary-configs-${{ matrix.parallel }}
runs-on: ["self-hosted", "1ES.Pool=1es-gha-citusdata-pool"]
container:
image: "${{ matrix.image_name }}:${{ fromJson(matrix.pg_version).full }}${{ needs.params.outputs.image_suffix }}"
options: --user root
needs:
- params
- build
strategy:
fail-fast: false
matrix:
image_name:
- ${{ needs.params.outputs.fail_test_image_name }}
pg_version:
- ${{ needs.params.outputs.pg15_version }}
- ${{ needs.params.outputs.pg16_version }}
- ${{ needs.params.outputs.pg17_version }}
parallel: [0,1,2,3,4,5] # workaround for running 6 parallel jobs
steps:
- uses: actions/checkout@v4
- uses: "./.github/actions/setup_extension"
- name: Test arbitrary configs
run: |-
# we use parallel jobs to split the tests into 6 parts and run them in parallel
# the script below extracts the tests for the current job
N=6 # Total number of jobs (see matrix.parallel)
X=${{ matrix.parallel }} # Current job number
TESTS=$(src/test/regress/citus_tests/print_test_names.py |
tr '\n' ',' | awk -v N="$N" -v X="$X" -F, '{
split("", parts)
for (i = 1; i <= NF; i++) {
parts[i % N] = parts[i % N] $i ","
}
print substr(parts[X], 1, length(parts[X])-1)
}')
echo $TESTS
gosu circleci \
make -C src/test/regress \
check-arbitrary-configs parallel=4 CONFIGS=$TESTS
- uses: "./.github/actions/save_logs_and_results"
if: always()
with:
folder: ${{ env.PG_MAJOR }}_arbitrary_configs_${{ matrix.parallel }}
- uses: "./.github/actions/upload_coverage"
if: always()
with:
flags: ${{ env.PG_MAJOR }}_arbitrary_configs_${{ matrix.parallel }}
codecov_token: ${{ secrets.CODECOV_TOKEN }}
test-pg-upgrade:
name: PG${{ matrix.old_pg_major }}-PG${{ matrix.new_pg_major }} - check-pg-upgrade
runs-on: ubuntu-latest
container:
image: "${{ needs.params.outputs.pgupgrade_image_name }}:${{ needs.params.outputs.upgrade_pg_versions }}${{ needs.params.outputs.image_suffix }}"
options: --user root
needs:
- params
- build
strategy:
fail-fast: false
matrix:
include:
- old_pg_major: 15
new_pg_major: 16
- old_pg_major: 16
new_pg_major: 17
- old_pg_major: 15
new_pg_major: 17
env:
old_pg_major: ${{ matrix.old_pg_major }}
new_pg_major: ${{ matrix.new_pg_major }}
steps:
- uses: actions/checkout@v4
- uses: "./.github/actions/setup_extension"
with:
pg_major: "${{ env.old_pg_major }}"
- uses: "./.github/actions/setup_extension"
with:
pg_major: "${{ env.new_pg_major }}"
- name: Install and test postgres upgrade
run: |-
gosu circleci \
make -C src/test/regress \
check-pg-upgrade \
old-bindir=/usr/lib/postgresql/${{ env.old_pg_major }}/bin \
new-bindir=/usr/lib/postgresql/${{ env.new_pg_major }}/bin
- name: Copy pg_upgrade logs for newData dir
run: |-
mkdir -p /tmp/pg_upgrade_newData_logs
if ls src/test/regress/tmp_upgrade/newData/*.log 1> /dev/null 2>&1; then
cp src/test/regress/tmp_upgrade/newData/*.log /tmp/pg_upgrade_newData_logs
fi
if: failure()
- uses: "./.github/actions/save_logs_and_results"
if: always()
with:
folder: ${{ env.old_pg_major }}_${{ env.new_pg_major }}_upgrade
- uses: "./.github/actions/upload_coverage"
if: always()
with:
flags: ${{ env.old_pg_major }}_${{ env.new_pg_major }}_upgrade
codecov_token: ${{ secrets.CODECOV_TOKEN }}
test-citus-upgrade:
name: PG${{ fromJson(needs.params.outputs.pg15_version).major }} - check-citus-upgrade
runs-on: ubuntu-latest
container:
image: "${{ needs.params.outputs.citusupgrade_image_name }}:${{ fromJson(needs.params.outputs.pg15_version).full }}${{ needs.params.outputs.image_suffix }}"
options: --user root
needs:
- params
- build
steps:
- uses: actions/checkout@v4
- uses: "./.github/actions/setup_extension"
with:
skip_installation: true
- name: Install and test citus upgrade
run: |-
# run make check-citus-upgrade for all citus versions
# the image has ${CITUS_VERSIONS} set with all verions it contains the binaries of
for citus_version in ${CITUS_VERSIONS}; do \
gosu circleci \
make -C src/test/regress \
check-citus-upgrade \
bindir=/usr/lib/postgresql/${PG_MAJOR}/bin \
citus-old-version=${citus_version} \
citus-pre-tar=/install-pg${PG_MAJOR}-citus${citus_version}.tar \
citus-post-tar=${GITHUB_WORKSPACE}/install-$PG_MAJOR.tar; \
done;
# run make check-citus-upgrade-mixed for all citus versions
# the image has ${CITUS_VERSIONS} set with all verions it contains the binaries of
for citus_version in ${CITUS_VERSIONS}; do \
gosu circleci \
make -C src/test/regress \
check-citus-upgrade-mixed \
citus-old-version=${citus_version} \
bindir=/usr/lib/postgresql/${PG_MAJOR}/bin \
citus-pre-tar=/install-pg${PG_MAJOR}-citus${citus_version}.tar \
citus-post-tar=${GITHUB_WORKSPACE}/install-$PG_MAJOR.tar; \
done;
- uses: "./.github/actions/save_logs_and_results"
if: always()
with:
folder: ${{ env.PG_MAJOR }}_citus_upgrade
- uses: "./.github/actions/upload_coverage"
if: always()
with:
flags: ${{ env.PG_MAJOR }}_citus_upgrade
codecov_token: ${{ secrets.CODECOV_TOKEN }}
upload-coverage:
if: always()
env:
CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
runs-on: ubuntu-latest
container:
image: ${{ needs.params.outputs.test_image_name }}:${{ fromJson(needs.params.outputs.pg17_version).full }}${{ needs.params.outputs.image_suffix }}
needs:
- params
- test-citus
- test-arbitrary-configs
- test-citus-upgrade
- test-pg-upgrade
steps:
- uses: actions/download-artifact@v4.1.8
with:
pattern: codeclimate*
path: codeclimate
merge-multiple: true
- name: Upload coverage results to Code Climate
run: |-
cc-test-reporter sum-coverage codeclimate/*.json -o total.json
cc-test-reporter upload-coverage -i total.json
ch_benchmark:
name: CH Benchmark
if: startsWith(github.ref, 'refs/heads/ch_benchmark/')
runs-on: ubuntu-latest
needs:
- build
steps:
- uses: actions/checkout@v4
- uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: install dependencies and run ch_benchmark tests
uses: azure/CLI@v1
with:
inlineScript: |
cd ./src/test/hammerdb
chmod +x run_hammerdb.sh
run_hammerdb.sh citusbot_ch_benchmark_rg
tpcc_benchmark:
name: TPCC Benchmark
if: startsWith(github.ref, 'refs/heads/tpcc_benchmark/')
runs-on: ubuntu-latest
needs:
- build
steps:
- uses: actions/checkout@v4
- uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: install dependencies and run tpcc_benchmark tests
uses: azure/CLI@v1
with:
inlineScript: |
cd ./src/test/hammerdb
chmod +x run_hammerdb.sh
run_hammerdb.sh citusbot_tpcc_benchmark_rg
prepare_parallelization_matrix_32:
name: Prepare parallelization matrix
if: ${{ needs.test-flakyness-pre.outputs.tests != ''}}
needs: test-flakyness-pre
runs-on: ubuntu-latest
outputs:
json: ${{ steps.parallelization.outputs.json }}
steps:
- uses: actions/checkout@v4
- uses: "./.github/actions/parallelization"
id: parallelization
with:
count: 32
test-flakyness-pre:
name: Detect regression tests need to be ran
if: ${{ !inputs.skip_test_flakyness }}}
runs-on: ubuntu-latest
needs: build
outputs:
tests: ${{ steps.detect-regression-tests.outputs.tests }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Detect regression tests need to be ran
id: detect-regression-tests
run: |-
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))
tests=${detected_changes}
# split the tests to be skipped --today we only skip upgrade tests
skipped_tests=""
not_skipped_tests=""
for test in $tests; do
if [[ $test =~ ^src/test/regress/sql/upgrade_ ]]; then
skipped_tests="$skipped_tests $test"
else
not_skipped_tests="$not_skipped_tests $test"
fi
done
if [ ! -z "$skipped_tests" ]; then
echo "Skipped tests " $skipped_tests
fi
if [ -z "$not_skipped_tests" ]; then
echo "Not detected any tests that flaky test detection should run"
else
echo "Detected tests " $not_skipped_tests
fi
echo 'tests<<EOF' >> $GITHUB_OUTPUT
echo "$not_skipped_tests" >> "$GITHUB_OUTPUT"
echo 'EOF' >> $GITHUB_OUTPUT
test-flakyness:
if: ${{ needs.test-flakyness-pre.outputs.tests != ''}}
name: Test flakyness
runs-on: ubuntu-latest
container:
image: ${{ needs.params.outputs.fail_test_image_name }}:${{ fromJson(needs.params.outputs.pg17_version).full }}${{ needs.params.outputs.image_suffix }}
options: --user root
env:
runs: 8
needs:
- params
- build
- test-flakyness-pre
- prepare_parallelization_matrix_32
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.prepare_parallelization_matrix_32.outputs.json) }}
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4.1.8
- uses: "./.github/actions/setup_extension"
- name: Run minimal tests
run: |-
tests="${{ needs.test-flakyness-pre.outputs.tests }}"
tests_array=($tests)
for test in "${tests_array[@]}"
do
test_name=$(echo "$test" | sed -r "s/.+\/(.+)\..+/\1/")
gosu circleci src/test/regress/citus_tests/run_test.py $test_name --repeat ${{ env.runs }} --use-whole-schedule-line
done
shell: bash
- uses: "./.github/actions/save_logs_and_results"
if: always()
with:
folder: test_flakyness_parallel_${{ matrix.id }}

View File

@ -21,10 +21,10 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
@ -76,4 +76,4 @@ jobs:
sudo make install-all
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
uses: github/codeql-action/analyze@v2

View File

@ -1,54 +0,0 @@
name: "Build devcontainer"
# Since building of containers can be quite time consuming, and take up some storage,
# there is no need to finish a build for a tag if new changes are concurrently being made.
# This cancels any previous builds for the same tag, and only the latest one will be kept.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on:
push:
paths:
- ".devcontainer/**"
workflow_dispatch:
jobs:
docker:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
attestations: write
id-token: write
steps:
-
name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: |
ghcr.io/citusdata/citus-devcontainer
tags: |
type=ref,event=branch
type=sha
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
-
name: 'Login to GitHub Container Registry'
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{github.actor}}
password: ${{secrets.GITHUB_TOKEN}}
-
name: Build and push
uses: docker/build-push-action@v5
with:
context: "{{defaultContext}}:.devcontainer"
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

View File

@ -1,79 +0,0 @@
name: Flaky test debugging
run-name: Flaky test debugging - ${{ inputs.flaky_test }} (${{ inputs.flaky_test_runs_per_job }}x${{ inputs.flaky_test_parallel_jobs }})
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
on:
workflow_dispatch:
inputs:
flaky_test:
required: true
type: string
description: Test to run
flaky_test_runs_per_job:
required: false
default: 8
type: number
description: Number of times to run the test
flaky_test_parallel_jobs:
required: false
default: 32
type: number
description: Number of parallel jobs to run
jobs:
build:
name: Build Citus
runs-on: ubuntu-latest
container:
image: ${{ vars.build_image_name }}:${{ vars.pg15_version }}${{ vars.image_suffix }}
options: --user root
steps:
- uses: actions/checkout@v4
- name: Configure, Build, and Install
run: |
echo "PG_MAJOR=${PG_MAJOR}" >> $GITHUB_ENV
./ci/build-citus.sh
shell: bash
- uses: actions/upload-artifact@v4.6.0
with:
name: build-${{ env.PG_MAJOR }}
path: |-
./build-${{ env.PG_MAJOR }}/*
./install-${{ env.PG_MAJOR }}.tar
prepare_parallelization_matrix:
name: Prepare parallelization matrix
runs-on: ubuntu-latest
outputs:
json: ${{ steps.parallelization.outputs.json }}
steps:
- uses: actions/checkout@v4
- uses: "./.github/actions/parallelization"
id: parallelization
with:
count: ${{ inputs.flaky_test_parallel_jobs }}
test_flakyness:
name: Test flakyness
runs-on: ubuntu-latest
container:
image: ${{ vars.fail_test_image_name }}:${{ vars.pg15_version }}${{ vars.image_suffix }}
options: --user root
needs:
[build, prepare_parallelization_matrix]
env:
test: "${{ inputs.flaky_test }}"
runs: "${{ inputs.flaky_test_runs_per_job }}"
skip: false
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.prepare_parallelization_matrix.outputs.json) }}
steps:
- uses: actions/checkout@v4
- uses: "./.github/actions/setup_extension"
- name: Run minimal tests
run: |-
gosu circleci src/test/regress/citus_tests/run_test.py ${{ env.test }} --repeat ${{ env.runs }} --use-whole-schedule-line
shell: bash
- uses: "./.github/actions/save_logs_and_results"
if: always()
with:
folder: check_flakyness_parallel_${{ matrix.id }}

View File

@ -3,14 +3,9 @@ name: Build tests in packaging images
on:
pull_request:
types: [opened, reopened,synchronize]
merge_group:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
get_postgres_versions_from_file:
@ -19,22 +14,20 @@ jobs:
pg_versions: ${{ steps.get-postgres-versions.outputs.pg_versions }}
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
fetch-depth: 2
- name: Get Postgres Versions
id: get-postgres-versions
run: |
set -euxo pipefail
# Postgres versions are stored in .github/workflows/build_and_test.yml
# file in json strings with major and full keys.
# Below command extracts the versions and get the unique values.
pg_versions=$(cat .github/workflows/build_and_test.yml | grep -oE '"major": "[0-9]+", "full": "[0-9.]+"' | sed -E 's/"major": "([0-9]+)", "full": "([0-9.]+)"/\1/g' | sort | uniq | tr '\n', ',')
# Postgres versions are stored in .circleci/config.yml file in "build-[pg-version] format. Below command
# extracts the versions and get the unique values.
pg_versions=`grep -Eo 'build-[[:digit:]]{2}' .circleci/config.yml|sed -e "s/^build-//"|sort|uniq|tr '\n' ','| head -c -1`
pg_versions_array="[ ${pg_versions} ]"
echo "Supported PG Versions: ${pg_versions_array}"
# Below line is needed to set the output variable to be used in the next job
echo "pg_versions=${pg_versions_array}" >> $GITHUB_OUTPUT
shell: bash
rpm_build_tests:
name: rpm_build_tests
needs: get_postgres_versions_from_file
@ -47,8 +40,10 @@ jobs:
# For this reason, we need to use a "matrix" to generate names of
# rpm images, e.g. citus/packaging:centos-7-pg12
packaging_docker_image:
- oraclelinux-7
- oraclelinux-8
- almalinux-8
- centos-7
- centos-8
- almalinux-9
POSTGRES_VERSION: ${{ fromJson(needs.get_postgres_versions_from_file.outputs.pg_versions) }}
@ -58,7 +53,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Set Postgres and python parameters for rpm based distros
run: |
@ -78,18 +73,8 @@ jobs:
- name: Make
run: |
git config --global --add safe.directory ${GITHUB_WORKSPACE}
make CFLAGS="-Wno-missing-braces" -sj$(cat /proc/cpuinfo | grep "core id" | wc -l) 2>&1 | tee -a output.log
# Check the exit code of the make command
make_exit_code=${PIPESTATUS[0]}
# If the make command returned a non-zero exit code, exit with the same code
if [[ $make_exit_code -ne 0 ]]; then
echo "make command failed with exit code $make_exit_code"
exit $make_exit_code
fi
- name: Make install
run: |
make CFLAGS="-Wno-missing-braces" install 2>&1 | tee -a output.log
@ -100,6 +85,11 @@ jobs:
PACKAGING_DOCKER_IMAGE: ${{ matrix.packaging_docker_image }}
run: |
echo "Postgres version: ${POSTGRES_VERSION}"
## Install required packages to execute packaging tools for rpm based distros
yum install python3-pip python3-devel postgresql-devel -y
python3 -m pip install wheel
./.github/packaging/validate_build_output.sh "rpm"
deb_build_tests:
@ -116,10 +106,13 @@ jobs:
# for each deb based image and we use POSTGRES_VERSION to set
# PG_CONFIG variable in each of those runs.
packaging_docker_image:
- debian-buster-all
- debian-bookworm-all
- debian-bullseye-all
- ubuntu-bionic-all
- ubuntu-focal-all
- ubuntu-jammy-all
- ubuntu-kinetic-all
POSTGRES_VERSION: ${{ fromJson(needs.get_postgres_versions_from_file.outputs.pg_versions) }}
@ -129,7 +122,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Set pg_config path and python parameters for deb based distros
run: |
@ -148,22 +141,9 @@ jobs:
make clean
- name: Make
shell: bash
run: |
set -e
git config --global --add safe.directory ${GITHUB_WORKSPACE}
make -sj$(cat /proc/cpuinfo | grep "core id" | wc -l) 2>&1 | tee -a output.log
# Check the exit code of the make command
make_exit_code=${PIPESTATUS[0]}
# If the make command returned a non-zero exit code, exit with the same code
if [[ $make_exit_code -ne 0 ]]; then
echo "make command failed with exit code $make_exit_code"
exit $make_exit_code
fi
- name: Make install
run: |
make install 2>&1 | tee -a output.log
@ -174,4 +154,9 @@ jobs:
PACKAGING_DOCKER_IMAGE: ${{ matrix.packaging_docker_image }}
run: |
echo "Postgres version: ${POSTGRES_VERSION}"
apt-get update -y
## Install required packages to execute packaging tools for deb based distros
apt-get install python3-dev python3-pip -y
apt-get purge -y python3-yaml
./.github/packaging/validate_build_output.sh "deb"

4
.gitignore vendored
View File

@ -41,7 +41,6 @@ lib*.pc
/src/backend/distributed/cdc/build-cdc-*/*
/src/test/cdc/tmp_check/*
# temporary files vim creates
*.swp
@ -55,6 +54,3 @@ lib*.pc
# style related temporary outputs
*.uncrustify
.venv
# added output when modifying check_gucs_are_alphabetically_sorted.sh
guc.out

View File

@ -1,544 +1,3 @@
### citus v13.1.0 (May 30th, 2025) ###
* Adds `citus_stat_counters` view that can be used to query
stat counters that Citus collects while the feature is enabled, which is
controlled by citus.enable_stat_counters. `citus_stat_counters()` can be
used to query the stat counters for the provided database oid and
`citus_stat_counters_reset()` can be used to reset them for the provided
database oid or for the current database if nothing or 0 is provided (#7917)
* Adds `citus_nodes` view that displays the node name, port role, and "active"
for nodes in the cluster (#7968)
* Adds `citus_is_primary_node()` UDF to determine if the current node is a
primary node in the cluster (#7720)
* Adds support for propagating `GRANT/REVOKE` rights on table columns (#7918)
* Adds support for propagating `REASSIGN OWNED BY` commands (#7319)
* Adds support for propagating `CREATE`/`DROP` database from all nodes (#7240,
#7253, #7359)
* Propagates `SECURITY LABEL ON ROLE` statement from any node (#7508)
* Adds support for issuing role management commands from worker nodes (#7278)
* Adds support for propagating `ALTER USER RENAME` commands (#7204)
* Adds support for propagating `ALTER DATABASE <db_name> SET ..` commands
(#7181)
* Adds support for propagating `SECURITY LABEL` on tables and columns (#7956)
* Adds support for propagating `COMMENT ON <database>/<role>` commands (#7388)
* Moves some of the internal citus functions from `pg_catalog` to
`citus_internal` schema (#7473, #7470, #7466, 7456, 7450)
* Adjusts `max_prepared_transactions` only when it's set to default on PG >= 16
(#7712)
* Adds skip_qualify_public param to shard_name() UDF to allow qualifying for
"public" schema when needed (#8014)
* Allows `citus_*_size` on indexes on a distributed tables (#7271)
* Allows `GRANT ADMIN` to now also be `INHERIT` or `SET` in support of PG16
* Makes sure `worker_copy_table_to_node` errors out with Citus tables (#7662)
* Adds information to explain output when using
`citus.explain_distributed_queries=false` (#7412)
* Logs username in the failed connection message (#7432)
* Makes sure to avoid incorrectly pushing-down the outer joins between
distributed tables and recurring relations (like reference tables, local
tables and `VALUES(..)` etc.) prior to PG 17 (#7937)
* Prevents incorrectly pushing `nextval()` call down to workers to avoid using
incorrect sequence value for some types of `INSERT .. SELECT`s (#7976)
* Makes sure to prevent `INSERT INTO ... SELECT` queries involving subfield or
sublink, to avoid crashes (#7912)
* Makes sure to take improvement_threshold into the account
in `citus_add_rebalance_strategy()` (#7247)
* Makes sure to disallow creating a replicated distributed
table concurrently (#7219)
* Fixes a bug that causes omitting `CASCADE` clause for the commands sent to
workers for `REVOKE` commands on tables (#7958)
* Fixes an issue detected using address sanitizer (#7948, #7949)
* Fixes a bug in deparsing of shard query in case of "output-table column" name
conflict (#7932)
* Fixes a crash in columnar custom scan that happens when a columnar table is
used in a join (#7703)
* Fixes `MERGE` command when insert value does not have source distributed
column (#7627)
* Fixes performance issue when using `\d tablename` on a server with many
tables (#7577)
* Fixes performance issue in `GetForeignKeyOids` on systems with many
constraints (#7580)
* Fixes performance issue when distributing a table that depends on an
extension (#7574)
* Fixes performance issue when creating distributed tables if many already
exist (#7575)
* Fixes a crash caused by some form of `ALTER TABLE ADD COLUMN` statements. When
adding multiple columns, if one of the `ADD COLUMN` statements contains a
`FOREIGN` constraint ommitting the referenced
columns in the statement, a `SEGFAULT` occurs (#7522)
* Fixes assertion failure in maintenance daemon during Citus upgrades (#7537)
* Fixes segmentation fault when using `CASE WHEN` in `DO` block functions
(#7554)
* Fixes undefined behavior in `master_disable_node` due to argument mismatch
(#7492)
* Fixes incorrect propagating of `GRANTED BY` and `CASCADE/RESTRICT` clauses
for `REVOKE` statements (#7451)
* Fixes the incorrect column count after `ALTER TABLE` (#7379)
* Fixes timeout when underlying socket is changed for an inter-node connection
(#7377)
* Fixes memory leaks (#7441, #7440)
* Fixes leaking of memory and memory contexts when tracking foreign keys between
Citus tables (#7236)
* Fixes a potential segfault for background rebalancer (#7694)
* Fixes potential `NULL` dereference in casual clocks (#7704)
### citus v13.0.4 (May 29th, 2025) ###
* Fixes an issue detected using address sanitizer (#7966)
* Error out for queries with outer joins and pseudoconstant quals in versions
prior to PG 17 (#7937)
### citus v12.1.8 (May 29, 2025) ###
* Fixes a crash in left outer joins that can happen when there is an an
aggregate on a column from the inner side of the join (#7904)
* Fixes an issue detected using address sanitizer (#7965)
* Fixes a crash when executing a prepared CALL, which is not pure SQL but
available with some drivers like npgsql and jpgdbc (#7288)
### citus v13.0.3 (March 20th, 2025) ###
* Fixes a version bump issue in 13.0.2
### citus v13.0.2 (March 12th, 2025) ###
* Fixes a crash in columnar custom scan that happens when a columnar table is
used in a join. (#7647)
* Fixes a bug that breaks `UPDATE SET (...) = (SELECT some_func(),... )`
type of queries on Citus tables (#7914)
* Fixes a planning error caused by a redundant WHERE clause (#7907)
* Fixes a crash in left outer joins that can happen when there is an aggregate
on a column from the inner side of the join. (#7901)
* Fixes deadlock with transaction recovery that is possible during Citus
upgrades. (#7910)
* Fixes a bug that prevents inserting into Citus tables that uses
a GENERATED ALWAYS AS IDENTITY column. (#7920)
* Ensures that a MERGE command on a distributed table with a WHEN NOT MATCHED BY
SOURCE clause runs against all shards of the distributed table. (#7900)
* Fixes a bug that breaks router updates on distributed tables
when a reference table is used in the subquery (#7897)
### citus v12.1.7 (Feb 6, 2025) ###
* Fixes a crash that happens because of unsafe catalog access when re-assigning
the global pid after `application_name` changes (#7791)
* Prevents crashes when another extension skips executing the
`ClientAuthentication_hook` of Citus. (#7836)
### citus v13.0.1 (February 4th, 2025) ###
* Drops support for PostgreSQL 14 (#7753)
### citus v13.0.0 (January 22, 2025) ###
* Adds support for PostgreSQL 17 (#7699, #7661)
* Adds `JSON_TABLE()` support in distributed queries (#7816)
* Propagates `MERGE ... WHEN NOT MATCHED BY SOURCE` (#7807)
* Propagates `MEMORY` and `SERIALIZE` options of `EXPLAIN` (#7802)
* Adds support for identity columns in distributed partitioned tables (#7785)
* Allows specifying an access method for distributed partitioned tables (#7818)
* Allows exclusion constraints on distributed partitioned tables (#7733)
* Allows configuring sslnegotiation using `citus.node_conn_info` (#7821)
* Avoids wal receiver timeouts during large shard splits (#7229)
* Fixes a bug causing incorrect writing of data to target `MERGE` repartition
command (#7659)
* Fixes a crash that happens because of unsafe catalog access when re-assigning
the global pid after `application_name` changes (#7791)
* Fixes incorrect `VALID UNTIL` setting assumption made for roles when syncing
them to new nodes (#7534)
* Fixes segfault when calling distributed procedure with a parameterized
distribution argument (#7242)
* Fixes server crash when trying to execute `activate_node_snapshot()` on a
single-node cluster (#7552)
* Improves `citus_move_shard_placement()` to fail early if there is a new node
without reference tables yet (#7467)
### citus v12.1.6 (Nov 14, 2024) ###
* Propagates `SECURITY LABEL .. ON ROLE` statements (#7304)
* Fixes crash caused by running queries with window partition (#7718)
### citus v12.1.5 (July 17, 2024) ###
* Adds support for MERGE commands with single shard distributed target tables
(#7643)
* Fixes an error with MERGE commands when insert value does not have source
distribution column (#7627)
### citus v12.1.4 (May 28, 2024) ###
* Adds null check for node in HasRangeTableRef (#7604)
### citus v12.1.3 (April 18, 2024) ###
* Allows overwriting host name for all inter-node connections by
supporting "host" parameter in citus.node_conninfo (#7541)
* Avoids distributed deadlocks by changing the order in which the locks are
acquired for the target and reference tables (#7542)
* Fixes a performance issue when distributing a table that depends on an
extension (#7574)
* Fixes a performance issue when using "\d tablename" on a server with
many tables (#7577)
* Fixes a crash caused by some form of ALTER TABLE ADD COLUMN
statements. When adding multiple columns, if one of the ADD COLUMN
statements contains a FOREIGN constraint omitting the referenced
columns in the statement, a SEGFAULT was occurring. (#7522)
* Fixes a performance issue when creating distributed tables if many
already exist (#7575, #7579)
* Fixes a bug when hostname in pg_dist_node resolves to multiple IPs
(#7377)
* Fixes performance issue when tracking foreign key constraints on
systems with many constraints (#7578)
* Fixes segmentation fault when using CASE WHEN in DO block within
functions. (#7554)
* Fixes undefined behavior in master_disable_node due to argument
mismatch (#7492)
* Fixes some potential bugs by correctly marking some variables as
volatile (#7570)
* Logs username in the failed connection message (#7432)
### citus v11.0.10 (February 15, 2024) ###
* Removes pg_send_cancellation and all references (#7135)
### citus v12.1.2 (February 12, 2024) ###
* Fixes the incorrect column count after ALTER TABLE (#7379)
### citus v12.0.1 (July 11, 2023) ###
* Fixes incorrect default value assumption for VACUUM(PROCESS_TOAST) #7122)
* Fixes a bug that causes an unexpected error when adding a column
with a NULL constraint (#7093)
* Fixes a bug that could cause COPY logic to skip data in case of OOM (#7152)
* Fixes a bug with deleting colocation groups (#6929)
* Fixes memory and memory contexts leaks in Foreign Constraint Graphs (#7236)
* Fixes shard size bug with too many shards (#7018)
* Fixes the incorrect column count after ALTER TABLE (#7379)
* Improves citus_tables view performance (#7050)
* Makes sure to disallow creating a replicated distributed table
concurrently (#7219)
* Removes pg_send_cancellation and all references (#7135)
### citus v11.3.1 (February 12, 2024) ###
* Disallows MERGE when the query prunes down to zero shards (#6946)
* Fixes a bug related to non-existent objects in DDL commands (#6984)
* Fixes a bug that could cause COPY logic to skip data in case of OOM (#7152)
* Fixes a bug with deleting colocation groups (#6929)
* Fixes incorrect results on fetching scrollable with hold cursors (#7014)
* Fixes memory and memory context leaks in Foreign Constraint Graphs (#7236)
* Fixes replicate reference tables task fail when user is superuser (#6930)
* Fixes the incorrect column count after ALTER TABLE (#7379)
* Improves citus_shard_sizes performance (#7050)
* Makes sure to disallow creating a replicated distributed table
concurrently (#7219)
* Removes pg_send_cancellation and all references (#7135)
### citus v11.2.2 (February 12, 2024) ###
* Fixes a bug in background shard rebalancer where the replicate
reference tables task fails if the current user is not a superuser (#6930)
* Fixes a bug related to non-existent objects in DDL commands (#6984)
* Fixes a bug that could cause COPY logic to skip data in case of OOM (#7152)
* Fixes a bug with deleting colocation groups (#6929)
* Fixes incorrect results on fetching scrollable with hold cursors (#7014)
* Fixes memory and memory context leaks in Foreign Constraint Graphs (#7236)
* Fixes the incorrect column count after ALTER TABLE (#7379)
* Improves failure handling of distributed execution (#7090)
* Makes sure to disallow creating a replicated distributed table
concurrently (#7219)
* Removes pg_send_cancellation (#7135)
### citus v11.1.7 (February 12, 2024) ###
* Fixes memory and memory context leaks in Foreign Constraint Graphs (#7236)
* Fixes a bug related to non-existent objects in DDL commands (#6984)
* Fixes a bug that could cause COPY logic to skip data in case of OOM (#7152)
* Fixes a bug with deleting colocation groups (#6929)
* Fixes incorrect results on fetching scrollable with hold cursors (#7014)
* Fixes the incorrect column count after ALTER TABLE (#7379)
* Improves failure handling of distributed execution (#7090)
* Makes sure to disallow creating a replicated distributed table
concurrently (#7219)
* Removes pg_send_cancellation and all references (#7135)
### citus v11.0.9 (February 12, 2024) ###
* Fixes a bug that could cause COPY logic to skip data in case of OOM (#7152)
* Fixes a bug with deleting colocation groups (#6929)
* Fixes memory and memory context leaks in Foreign Constraint Graphs (#7236)
* Fixes the incorrect column count after ALTER TABLE (#7462)
* Improve failure handling of distributed execution (#7090)
### citus v12.1.1 (November 9, 2023) ###
* Fixes leaking of memory and memory contexts in Citus foreign key cache
(#7236)
* Makes sure to disallow creating a replicated distributed table concurrently
(#7219)
### citus v12.1.0 (September 12, 2023) ###
* Adds support for PostgreSQL 16.0 (#7173)
* Add `citus_schema_move()` function which moves tables within a
distributed schema to another node (#7180)
* Adds `citus_pause_node_within_txn()` UDF that allows pausing the node with
given id (#7089)
* Makes sure to enforce shard level colocation with the GUC
`citus.enable_non_colocated_router_query_pushdown` (#7076)
* Allows creating reference / distributed-schema tables from local tables added
to metadata and that use identity columns (#7131)
* Propagates `BUFFER_USAGE_LIMIT` option in `VACUUM` and `ANALYZE` (#7114)
* Propagates `PROCESS_MAIN`, `SKIP_DATABASE_STATS`, `ONLY_DATABASE_STATS`
options in `VACUUM` (#7114)
* Propagates `GENERIC_PLAN` option in `EXPLAIN` (#7141)
* Propagates "rules" option in `CREATE COLLATION` (#7185)
* Propagates `GRANT`/ `REVOKE` for database privileges (#7109)
* Adds TRUNCATE trigger support on Citus foreign tables (#7170)
* Removes `pg_send_cancellation` (#7135)
* Prevents unnecessarily pulling the data into coordinator for some
`INSERT .. SELECT` queries that target a single-shard group (#7077)
* Makes sure that rebalancer throws an error if replication factor is greater
than the shard allowed node count. Also makes sure to avoid moving a shard
to a node that it already exists on. (#7074)
* Fixes a bug that may appear during 2PC recovery when there are multiple
databases (#7174)
* Fixes a bug that could cause `COPY` logic to skip data in case of
out-of-memory (#7152)
* Fixes a bug that causes an unexpected error when adding a column with
a `NULL` constraint (#7093)
* Fixes `PROCESS_TOAST` default value to `true` (#7122)
* Improves the error thrown when there is datatype mismatch in `MERGE ON`
(#7081)
### citus v12.0.0 (July 11, 2023) ###
* Adds support for schema-based sharding.
While `citus.enable_schema_based_sharding` GUC allows sharding the database
based on newly created schemas, `citus_schema_distribute()` allows doing so
for the existing schemas. Distributed schemas used for sharding the database
can be listed by using the view `citus_schemas`, monitored by using the view
`citus_stat_schemas`, and undistributed by using the udf
`citus_schema_undistribute()`
(#6866, #6979, #6933, #6936 and many others)
* Supports MERGE command across non-colocated distributed tables/subqueries,
reference tables and joins on non-distribution columns (#6927)
* Drops PG13 Support (#7002, #7007)
* Changes default rebalance strategy to by_disk_size (#7033)
* Changes by_disk_size rebalance strategy to have a base size (#7035)
* Improves citus_tables view performance (#7018)
* Improves tenant monitoring performance (#6868)
* Introduces the GUC `citus.stat_tenants_untracked_sample_rate` for sampling in
tenant monitoring (#7026)
* Adds CPU usage to citus_stat_tenants (#6844)
* Propagates `ALTER SCHEMA .. OWNER TO ..` commands to worker (#6987)
* Allows `ADD COLUMN` in command string with other commands (#7032)
* Allows `DROP CONSTRAINT` in command string with other commands (#7012)
* Makes sure to properly handle index storage options for `ADD CONSTRAINT
`/ COLUMN commands (#7032)
* Makes sure to properly handle `IF NOT EXISTS` for `ADD COLUMN` commands
(#7032)
* Allows using generated identity column based on int/smallint when creating
a distributed table with the limitation of not being able perform DMLs on
identity columns from worker nodes (#7008)
* Supports custom cast from / to timestamptz in time partition management UDFs
(#6923)
* Optimizes pushdown planner on memory and cpu (#6945)
* Changes citus_shard_sizes view's table_name column to shard_id (#7003)
* The GUC search_path is now reported when it is updated (#6983)
* Disables citus.enable_non_colocated_router_query_pushdown GUC by default to
ensure generating a consistent distributed plan for the queries that
reference non-colocated distributed tables (#6909)
* Disallows MERGE with filters that prune down to zero shards (#6946)
* Makes sure to take `shouldhaveshards` setting into account for a node when
planning rebalance steps (#6887)
* Improves the compatibility with other extension by forwarding to existing
emit_log_hook in our log hook (#6877)
* Fixes wrong result when using `NOT MATCHED` with MERGE command (#6943)
* Fixes querying the view `citus_shard_sizes` when there are too many shards
(#7018)
* Fixes a bug related to type casts from other types to text/varchar (#6391)
* Fixes propagating `CREATE SCHEMA AUTHORIZATION ..` with no schema name
(#7015)
* Fixes an error when creating a FOREIGN KEY without a name referencing a schema
qualified table (#6986)
* Fixes a rare bug which mostly happens with queries that contain both outer
join and where clauses (#6857)
* Fixes a bug related to propagation of schemas when pg_dist_node is empty
(#6900)
* Fixes a crash when a query is locally executed with explain analyze (#6892)
### citus v11.3.0 (May 2, 2023) ###
* Introduces CDC implementation for Citus using logical replication
@ -547,7 +6,7 @@ available with some drivers like npgsql and jpgdbc (#7288)
* Adds support for `MERGE` command on co-located distributed tables joined on
distribution column (#6696, #6733)
* Adds the view `citus_stat_tenants` that monitor statistics on tenant usages
* Adds the view `citus_stats_tenants` that monitor statistics on tenant usages
(#6725)
* Adds the GUC `citus.max_background_task_executors_per_node` to control number

View File

@ -1,9 +0,0 @@
# Microsoft Open Source Code of Conduct
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
Resources:
- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)
- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns

View File

@ -11,52 +11,6 @@ sign a Contributor License Agreement (CLA). For an explanation of
why we ask this as well as instructions for how to proceed, see the
[Microsoft CLA](https://cla.opensource.microsoft.com/).
### Devcontainer / Github Codespaces
The 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).
With the extension installed you can run the following from the command pallet to get started
```
> Dev Containers: Clone Repository in Container Volume...
```
In the subsequent popup paste the url to the repo and hit enter.
```
https://github.com/citusdata/citus
```
This 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.
To 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.
To 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.
With 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.
### Debugging in the VS code
1. Start Debugging: Press F5 in VS Code to start debugging. When prompted, you'll need to attach the debugger to the appropriate PostgreSQL process.
2. Identify the Process: If you're running a psql command, take note of the PID that appears in your psql prompt. For example:
```
[local] citus@citus:9700 (PID: 5436)=#
```
This PID (5436 in this case) indicates the process that you should attach the debugger to.
If you are uncertain about which process to attach, you can list all running PostgreSQL processes using the following command:
```
ps aux | grep postgres
```
Look for the process associated with the PID you noted. For example:
```
citus 5436 0.0 0.0 0 0 ? S 14:00 0:00 postgres: citus citus
```
4. 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.
5. 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.
### Getting and building
[PostgreSQL documentation](https://www.postgresql.org/support/versioning/) has a
@ -87,8 +41,6 @@ that are missing in earlier minor versions.
cd citus
./configure
# If you have already installed the project, you need to clean it first
make clean
make
make install
# Optionally, you might instead want to use `make install-all`
@ -127,8 +79,6 @@ that are missing in earlier minor versions.
git clone https://github.com/citusdata/citus.git
cd citus
./configure
# If you have already installed the project previously, you need to clean it first
make clean
make
sudo make install
# Optionally, you might instead want to use `sudo make install-all`
@ -179,8 +129,6 @@ that are missing in earlier minor versions.
git clone https://github.com/citusdata/citus.git
cd citus
PG_CONFIG=/usr/pgsql-14/bin/pg_config ./configure
# If you have already installed the project previously, you need to clean it first
make clean
make
sudo make install
# Optionally, you might instead want to use `sudo make install-all`
@ -197,7 +145,43 @@ that are missing in earlier minor versions.
### Following our coding conventions
Our coding conventions are documented in [STYLEGUIDE.md](STYLEGUIDE.md).
CircleCI will automatically reject any PRs which do not follow our coding
conventions. The easiest way to ensure your PR adheres to those conventions is
to use the [citus_indent](https://github.com/citusdata/tools/tree/develop/uncrustify)
tool. This tool uses `uncrustify` under the hood.
```bash
# Uncrustify changes the way it formats code every release a bit. To make sure
# everyone formats consistently we use version 0.68.1:
curl -L https://github.com/uncrustify/uncrustify/archive/uncrustify-0.68.1.tar.gz | tar xz
cd uncrustify-uncrustify-0.68.1/
mkdir build
cd build
cmake ..
make -j5
sudo make install
cd ../..
git clone https://github.com/citusdata/tools.git
cd tools
make uncrustify/.install
```
Once you've done that, you can run the `make reindent` command from the top
directory to recursively check and correct the style of any source files in the
current directory. Under the hood, `make reindent` will run `citus_indent` and
some other style corrections for you.
You can also run the following in the directory of this repository to
automatically format all the files that you have changed before committing:
```bash
cat > .git/hooks/pre-commit << __EOF__
#!/bin/bash
citus_indent --check --diff || { citus_indent --diff; exit 1; }
__EOF__
chmod +x .git/hooks/pre-commit
```
### Making SQL changes
@ -235,49 +219,6 @@ style `#include` statements like this:
Any other SQL you can put directly in the main sql file, e.g.
`src/backend/distributed/sql/citus--8.3-1--9.0-1.sql`.
### Backporting a commit to a release branch
1. Check out the release branch that you want to backport to `git checkout release-11.3`
2. Make sure you have the latest changes `git pull`
3. Create a new release branch with a unique name `git checkout -b release-11.3-<yourname>`
4. Cherry-pick the commit that you want to backport `git cherry-pick -x <sha>` (the `-x` is important)
5. Push the branch `git push`
6. Wait for tests to pass
7. If the cherry-pick required non-trivial merge conflicts, create a PR and ask
for a review.
8. After the tests pass on CI, fast-forward the release branch `git push origin release-11.3-<yourname>:release-11.3`
### Running tests
See [`src/test/regress/README.md`](https://github.com/citusdata/citus/blob/master/src/test/regress/README.md)
### Documentation
User-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/).
Detailed 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.
# Making a pull request ready for reviews
Asking 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.
But 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:
* Your changes don't perform **unnecessary line addition / deletions / style changes on unrelated files / lines**.
* 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.
* Your PR has necessary amount of **tests** and that they're passing.
* You separated as much as possible work into **separate PRs**, e.g., a prerequisite bugfix, a refactoring etc..
* Your PR doesn't introduce a typo or something that you can easily fix yourself.
* 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.
* And finally, perform a **self-review** to make sure that:
* Code and code-comments reflects the idea **without requiring an extra explanation** via a chat message / email / PR comment.
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.
* PR description is clear enough.
* 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>`.
* **Commit messages** are clear enough if the commits are doing logically different things.

View File

@ -1,43 +0,0 @@
# Devcontainer
## Coredumps
When 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.
To verify if corefiles are written run the following command in a terminal. This shows the filename pattern with which the corefile will be written.
```bash
cat /proc/sys/kernel/core_pattern
```
This 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.
You 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;
- You either include `%p` in the core_pattern. This gets substituted with the PID of the crashing process.
- 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.
When 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.
### Windows (docker desktop)
When 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.
```powershell
wsl --list
```
Among others this should list both `docker-desktop` and `docker-desktop-data`. You can then open a shell in the `docker-desktop` environment.
```powershell
wsl -d docker-desktop
```
Inside this shell you can verify that you have the right environment by running
```bash
cat /proc/sys/kernel/core_pattern
```
This should show the same configuration as the one you see inside the devcontainer. You can then change the setting by running the following command.
This 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.
```bash
echo "core" > /proc/sys/kernel/core_pattern
```

View File

@ -11,7 +11,7 @@ endif
include Makefile.global
all: extension
all: extension pg_send_cancellation
# build columnar only
@ -40,14 +40,22 @@ clean-full:
install-downgrades:
$(MAKE) -C src/backend/distributed/ install-downgrades
install-all: install-headers
install-all: install-headers install-pg_send_cancellation
$(MAKE) -C src/backend/columnar/ install-all
$(MAKE) -C src/backend/distributed/ install-all
# build citus_send_cancellation binary
pg_send_cancellation:
$(MAKE) -C src/bin/pg_send_cancellation/ all
install-pg_send_cancellation: pg_send_cancellation
$(MAKE) -C src/bin/pg_send_cancellation/ install
clean-pg_send_cancellation:
$(MAKE) -C src/bin/pg_send_cancellation/ clean
.PHONY: pg_send_cancellation install-pg_send_cancellation clean-pg_send_cancellation
# Add to generic targets
install: install-extension install-headers
clean: clean-extension
install: install-extension install-headers install-pg_send_cancellation
clean: clean-extension clean-pg_send_cancellation
# apply or check style
reindent:
@ -61,7 +69,6 @@ check-style:
# depend on install-all so that downgrade scripts are installed as well
check: all install-all
# explicetely does not use $(MAKE) to avoid parallelism
make -C src/test/regress check
$(MAKE) -C src/test/regress check-full
.PHONY: all check clean install install-downgrades install-all

View File

@ -1,14 +1,14 @@
| **<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/>**|
| **<br/>The Citus database is 100% open source.<br/><img width=1000/><br/>Learn what's new in the [Citus 11.2 release blog](https://www.citusdata.com/blog/2023/02/08/whats-new-in-citus-11-2-patroni-ha-support/) and the [Citus Updates page](https://www.citusdata.com/updates/).<br/><br/>**|
|---|
<br/>
![Citus Banner](images/citus-readme-banner.png)
![Citus Banner](/citus-readme-banner.png)
[![Latest Docs](https://img.shields.io/badge/docs-latest-brightgreen.svg)](https://docs.citusdata.com/)
[![Stack Overflow](https://img.shields.io/badge/Stack%20Overflow-%20-545353?logo=Stack%20Overflow)](https://stackoverflow.com/questions/tagged/citus)
[![Slack](https://cituscdn.azureedge.net/images/social/slack-badge.svg)](https://slack.citusdata.com/)
[![Slack Status](https://citus-slack.herokuapp.com/badge.svg)](https://citus-public.slack.com/)
[![Code Coverage](https://codecov.io/gh/citusdata/citus/branch/master/graph/badge.svg)](https://app.codecov.io/gh/citusdata/citus)
[![Twitter](https://img.shields.io/twitter/follow/citusdata.svg?label=Follow%20@citusdata)](https://twitter.com/intent/follow?screen_name=citusdata)
@ -31,14 +31,13 @@ You can use these Citus superpowers to make your Postgres database scale-out rea
Our [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.
![Citus scales out from a single node](images/citus-scale-out.png)
![Citus scales out from a single node](/citus-scale-out.png)
Since 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.
- [Why Citus?](#why-citus)
- [Getting Started](#getting-started)
- [Using Citus](#using-citus)
- [Schema-based sharding](#schema-based-sharding)
- [Setting up with High Availability](#setting-up-with-high-availability)
- [Documentation](#documentation)
- [Architecture](#architecture)
@ -95,14 +94,14 @@ Install packages on Ubuntu / Debian:
```bash
curl https://install.citusdata.com/community/deb.sh > add-citus-repo.sh
sudo bash add-citus-repo.sh
sudo apt-get -y install postgresql-17-citus-13.0
sudo apt-get -y install postgresql-15-citus-11.2
```
Install packages on Red Hat:
Install packages on CentOS / Red Hat:
```bash
curl https://install.citusdata.com/community/rpm.sh > add-citus-repo.sh
sudo bash add-citus-repo.sh
sudo yum install -y citus130_17
sudo yum install -y citus112_15
```
To add Citus to your local PostgreSQL database, add the following to `postgresql.conf`:
@ -348,48 +347,9 @@ When using columnar storage, you should only load data in batch using `COPY` or
To learn more about columnar storage, check out the [columnar storage README](https://github.com/citusdata/citus/blob/master/src/backend/columnar/README.md).
## Schema-based sharding
Available 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.
### Creating distributed schemas
You can turn an existing schema into a distributed schema by calling `citus_schema_distribute`:
```sql
SELECT citus_schema_distribute('user_service');
```
Alternatively, you can set `citus.enable_schema_based_sharding` to have all newly created schemas be automatically converted into distributed schemas:
```sql
SET citus.enable_schema_based_sharding TO ON;
CREATE SCHEMA AUTHORIZATION user_service;
CREATE SCHEMA AUTHORIZATION time_service;
CREATE SCHEMA AUTHORIZATION ping_service;
```
### Running queries
Queries will be properly routed to schemas based on `search_path` or by explicitly using the schema name in the query.
For [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.
```sql
CREATE USER user_service;
CREATE SCHEMA AUTHORIZATION user_service;
```
For typical multi-tenant applications, you would set the search path to the tenant schema name in your application:
```sql
SET search_path = tenant_name, public;
```
## Setting up with High Availability
One 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.
One 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 Citus 11.2 ships with improvements for smoother node switchover in Patroni.
An example of patronictl list output for the Citus cluster:
@ -423,14 +383,12 @@ A Citus database cluster grows from a single PostgreSQL node into a cluster by a
Data 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.
![Citus architecture](images/citus-architecture.png)
![Citus architecture](/citus-architecture.png)
When 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.
The 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.
Detailed descriptions of the implementation for Citus developers are provided in the [Citus Technical Documentation](src/backend/distributed/README.md).
## When to use Citus
Citus is uniquely capable of scaling both analytical and transactional workloads with up to petabytes of data. Use cases in which Citus is commonly used:
@ -440,23 +398,21 @@ Citus is uniquely capable of scaling both analytical and transactional workloads
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.
Example real-time analytics users: [Algolia](https://www.citusdata.com/customers/algolia)
Example real-time analytics users: [Algolia](https://www.citusdata.com/customers/algolia), [Heap](https://www.citusdata.com/customers/heap)
- **[Time series data](http://docs.citusdata.com/en/stable/use_cases/timeseries.html)**:
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.
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 Cituss parallel, distributed query engine for fast analytical queries, and use the built-in *columnar storage* to compress old partitions.
Example users: [MixRank](https://www.citusdata.com/customers/mixrank)
Example users: [MixRank](https://www.citusdata.com/customers/mixrank), [Windows team](https://techcommunity.microsoft.com/t5/azure-database-for-postgresql/architecting-petabyte-scale-analytics-by-scaling-out-postgres-on/ba-p/969685)
- **[Software-as-a-service (SaaS) applications](http://docs.citusdata.com/en/stable/use_cases/multi_tenant.html)**:
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.
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.
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)
- **[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 cant 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.
Example multi-tenant SaaS users: [Copper](https://www.citusdata.com/customers/copper), [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)
- **Geospatial**:
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.
@ -475,12 +431,6 @@ Citus is uniquely capable of scaling both analytical and transactional workloads
Citus 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.
## Code of Conduct
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
## Stay Connected
- **Twitter**: Follow us [@citusdata](https://twitter.com/citusdata) to track the latest posts & updates on whats happening.

View File

@ -1,160 +0,0 @@
# Coding style
The 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.
## Using citus_indent
CI pipeline will automatically reject any PRs which do not follow our coding
conventions. The easiest way to ensure your PR adheres to those conventions is
to use the [citus_indent](https://github.com/citusdata/tools/tree/develop/uncrustify)
tool. This tool uses `uncrustify` under the hood.
```bash
# Uncrustify changes the way it formats code every release a bit. To make sure
# everyone formats consistently we use version 0.68.1:
curl -L https://github.com/uncrustify/uncrustify/archive/uncrustify-0.68.1.tar.gz | tar xz
cd uncrustify-uncrustify-0.68.1/
mkdir build
cd build
cmake ..
make -j5
sudo make install
cd ../..
git clone https://github.com/citusdata/tools.git
cd tools
make uncrustify/.install
```
Once you've done that, you can run the `make reindent` command from the top
directory to recursively check and correct the style of any source files in the
current directory. Under the hood, `make reindent` will run `citus_indent` and
some other style corrections for you.
You can also run the following in the directory of this repository to
automatically format all the files that you have changed before committing:
```bash
cat > .git/hooks/pre-commit << __EOF__
#!/bin/bash
citus_indent --check --diff || { citus_indent --diff; exit 1; }
__EOF__
chmod +x .git/hooks/pre-commit
```
## Other rules we follow that citus_indent does not enforce
* We almost always use **CamelCase**, when naming functions, variables etc., **not snake_case**.
* 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:
```c
bool IsCitusExtensionLoaded = false;
bool
IsAlterTableRenameStmt(RenameStmt *renameStmt)
{
AlterTableCmd *alterTableCommand = NULL;
..
..
bool isAlterTableRenameStmt = false;
..
}
```
* We **start functions with a comment**:
```c
/*
* MyNiceFunction <something in present simple tense, e.g., processes / returns / checks / takes X as input / does Y> ..
* <some more nice words> ..
* <some more nice words> ..
*/
<static?> <return type>
MyNiceFunction(..)
{
..
..
}
```
* `#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:
* System includes (eg. #include<...>)
* Postgres.h (eg. #include "postgres.h")
* Toplevel imports from postgres, not contained in a directory (eg. #include "miscadmin.h")
* General postgres includes (eg . #include "nodes/...")
* Toplevel citus includes, not contained in a directory (eg. #include "citus_verion.h")
* Columnar includes (eg. #include "columnar/...")
* Distributed includes (eg. #include "distributed/...")
* Comments:
```c
/* single line comments start with a lower-case */
/*
* We start multi-line comments with a capital letter
* and keep adding a star to the beginning of each line
* until we close the comment with a star and a slash.
*/
```
* Order of function implementations and their declarations in a file:
We define static functions after the functions that call them. For example:
```c
#include<..>
#include<..>
..
..
typedef struct
{
..
..
} MyNiceStruct;
..
..
PG_FUNCTION_INFO_V1(my_nice_udf1);
PG_FUNCTION_INFO_V1(my_nice_udf2);
..
..
// .. somewhere on top of the file …
static void MyNiceStaticlyDeclaredFunction1(…);
static void MyNiceStaticlyDeclaredFunction2(…);
..
..
void
MyNiceFunctionExternedViaHeaderFile(..)
{
..
..
MyNiceStaticlyDeclaredFunction1(..);
..
..
MyNiceStaticlyDeclaredFunction2(..);
..
}
..
..
// we define this first because it's called by MyNiceFunctionExternedViaHeaderFile()
// before MyNiceStaticlyDeclaredFunction2()
static void
MyNiceStaticlyDeclaredFunction1(…)
{
}
..
..
// then we define this
static void
MyNiceStaticlyDeclaredFunction2(…)
{
}
```

View File

@ -385,18 +385,3 @@ definitions are in alphabetical order.
## `print_stack_trace.sh`
This script prints stack traces for failed tests, if they left core files.
## `sort_and_group_includes.sh`
This script checks and fixes issues with include grouping and sorting in C files.
Includes are grouped in the following groups:
- System includes (eg. `#include <math>`)
- Postgres.h include (eg. `#include "postgres.h"`)
- Toplevel postgres includes (includes not in a directory eg. `#include "miscadmin.h`)
- Postgres includes in a directory (eg. `#include "catalog/pg_type.h"`)
- Toplevel citus includes (includes not in a directory eg. `#include "pg_version_constants.h"`)
- Columnar includes (eg. `#include "columnar/columnar.h"`)
- Distributed includes (eg. `#include "distributed/maintenanced.h"`)
Within every group the include lines are sorted alphabetically.

View File

@ -15,6 +15,9 @@ PG_MAJOR=${PG_MAJOR:?please provide the postgres major version}
codename=${VERSION#*(}
codename=${codename%)*}
# get project from argument
project="${CIRCLE_PROJECT_REPONAME}"
# we'll do everything with absolute paths
basedir="$(pwd)"
@ -25,7 +28,7 @@ build_ext() {
pg_major="$1"
builddir="${basedir}/build-${pg_major}"
echo "Beginning build for PostgreSQL ${pg_major}..." >&2
echo "Beginning build of ${project} for PostgreSQL ${pg_major}..." >&2
# do everything in a subdirectory to avoid clutter in current directory
mkdir -p "${builddir}" && cd "${builddir}"

View File

@ -14,8 +14,8 @@ ci_scripts=$(
grep -v -E '^(ci_helpers.sh|fix_style.sh)$'
)
for script in $ci_scripts; do
if ! grep "\\bci/$script\\b" -r .github > /dev/null; then
echo "ERROR: CI script with name \"$script\" is not actually used in .github folder"
if ! grep "\\bci/$script\\b" .circleci/config.yml > /dev/null; then
echo "ERROR: CI script with name \"$script\" is not actually used in .circleci/config.yml"
exit 1
fi
if ! grep "^## \`$script\`\$" ci/README.md > /dev/null; then

96
ci/check_enterprise_merge.sh Executable file
View File

@ -0,0 +1,96 @@
#!/bin/bash
# Testing this script locally requires you to set the following environment
# variables:
# CIRCLE_BRANCH, GIT_USERNAME and GIT_TOKEN
# fail if trying to reference a variable that is not set.
set -u
# exit immediately if a command fails
set -e
# Fail on pipe failures
set -o pipefail
PR_BRANCH="${CIRCLE_BRANCH}"
ENTERPRISE_REMOTE="https://${GIT_USERNAME}:${GIT_TOKEN}@github.com/citusdata/citus-enterprise"
# shellcheck disable=SC1091
source ci/ci_helpers.sh
# List executed commands. This is done so debugging this script is easier when
# it fails. It's explicitly done after git remote add so username and password
# are not shown in CI output (even though it's also filtered out by CircleCI)
set -x
check_compile () {
echo "INFO: checking if merged code can be compiled"
./configure --without-libcurl
make -j10
}
# Clone current git repo (which should be community) to a temporary working
# directory and go there
GIT_DIR_ROOT="$(git rev-parse --show-toplevel)"
TMP_GIT_DIR="$(mktemp --directory -t citus-merge-check.XXXXXXXXX)"
git clone "$GIT_DIR_ROOT" "$TMP_GIT_DIR"
cd "$TMP_GIT_DIR"
# Fails in CI without this
git config user.email "citus-bot@microsoft.com"
git config user.name "citus bot"
# Disable "set -x" temporarily, because $ENTERPRISE_REMOTE contains passwords
{ set +x ; } 2> /dev/null
git remote add enterprise "$ENTERPRISE_REMOTE"
set -x
git remote set-url --push enterprise no-pushing
# Fetch enterprise-master
git fetch enterprise enterprise-master
git checkout "enterprise/enterprise-master"
if git merge --no-commit "origin/$PR_BRANCH"; then
echo "INFO: community PR branch could be merged into enterprise-master"
# check that we can compile after the merge
if check_compile; then
exit 0
fi
echo "WARN: Failed to compile after community PR branch was merged into enterprise"
fi
# undo partial merge
git merge --abort
# If we have a conflict on enterprise merge on the master branch, we have a problem.
# Provide an error message to indicate that enterprise merge is needed to fix this check.
if [[ $PR_BRANCH = master ]]; then
echo "ERROR: Master branch has merge conflicts with enterprise-master."
echo "Try re-running this CI job after merging your changes into enterprise-master."
exit 1
fi
if ! git fetch enterprise "$PR_BRANCH" ; then
echo "ERROR: enterprise/$PR_BRANCH was not found and community PR branch could not be merged into enterprise-master"
exit 1
fi
# Show the top commit of the enterprise PR branch to make debugging easier
git log -n 1 "enterprise/$PR_BRANCH"
# Check that this branch contains the top commit of the current community PR
# branch. If it does not it means it's not up to date with the current PR, so
# the enterprise branch should be updated.
if ! git merge-base --is-ancestor "origin/$PR_BRANCH" "enterprise/$PR_BRANCH" ; then
echo "ERROR: enterprise/$PR_BRANCH is not up to date with community PR branch"
exit 1
fi
# Now check if we can merge the enterprise PR into enterprise-master without
# issues.
git merge --no-commit "enterprise/$PR_BRANCH"
# check that we can compile after the merge
check_compile

View File

@ -4,22 +4,7 @@ set -euo pipefail
# shellcheck disable=SC1091
source ci/ci_helpers.sh
# Find the line that exactly matches "RegisterCitusConfigVariables(void)" in
# shared_library_init.c. grep command returns something like
# "934:RegisterCitusConfigVariables(void)" and we extract the line number
# with cut.
RegisterCitusConfigVariables_begin_linenumber=$(grep -n "^RegisterCitusConfigVariables(void)$" src/backend/distributed/shared_library_init.c | cut -d: -f1)
# Consider the lines starting from $RegisterCitusConfigVariables_begin_linenumber,
# grep the first line that starts with "}" and extract the line number with cut
# as in the previous step.
RegisterCitusConfigVariables_length=$(tail -n +$RegisterCitusConfigVariables_begin_linenumber src/backend/distributed/shared_library_init.c | grep -n -m 1 "^}$" | cut -d: -f1)
# extract the function definition of RegisterCitusConfigVariables into a temp file
tail -n +$RegisterCitusConfigVariables_begin_linenumber src/backend/distributed/shared_library_init.c | head -n $(($RegisterCitusConfigVariables_length)) > RegisterCitusConfigVariables_func_def.out
# extract citus gucs in the form of <tab><tab>"citus.X"
grep -P "^[\t][\t]\"citus\.[a-zA-Z_0-9]+\"" RegisterCitusConfigVariables_func_def.out > gucs.out
LC_COLLATE=C sort -c gucs.out
# extract citus gucs in the form of "citus.X"
grep -o -E "(\.*\"citus.\w+\")," src/backend/distributed/shared_library_init.c > gucs.out
sort -c gucs.out
rm gucs.out
rm RegisterCitusConfigVariables_func_def.out

View File

@ -19,4 +19,3 @@ ci/disallow_long_changelog_entries.sh
ci/normalize_expected.sh
ci/fix_gitignore.sh
ci/print_stack_trace.sh
ci/sort_and_group_includes.sh

View File

@ -1,157 +0,0 @@
#!/usr/bin/env python3
"""
easy command line to run against all citus-style checked files:
$ git ls-files \
| git check-attr --stdin citus-style \
| grep 'citus-style: set' \
| awk '{print $1}' \
| cut -d':' -f1 \
| xargs -n1 ./ci/include_grouping.py
"""
import collections
import os
import sys
def main(args):
if len(args) < 2:
print("Usage: include_grouping.py <file>")
return
file = args[1]
if not os.path.isfile(file):
sys.exit(f"File '{file}' does not exist")
with open(file, "r") as in_file:
with open(file + ".tmp", "w") as out_file:
includes = []
skipped_lines = []
# This calls print_sorted_includes on a set of consecutive #include lines.
# This implicitly keeps separation of any #include lines that are contained in
# an #ifdef, because it will order the #include lines inside and after the
# #ifdef completely separately.
for line in in_file:
# if a line starts with #include we don't want to print it yet, instead we
# want to collect all consecutive #include lines
if line.startswith("#include"):
includes.append(line)
skipped_lines = []
continue
# if we have collected any #include lines, we want to print them sorted
# before printing the current line. However, if the current line is empty
# we want to perform a lookahead to see if the next line is an #include.
# To maintain any separation between #include lines and their subsequent
# lines we keep track of all lines we have skipped inbetween.
if len(includes) > 0:
if len(line.strip()) == 0:
skipped_lines.append(line)
continue
# we have includes that need to be grouped before printing the current
# line.
print_sorted_includes(includes, file=out_file)
includes = []
# print any skipped lines
print("".join(skipped_lines), end="", file=out_file)
skipped_lines = []
print(line, end="", file=out_file)
# move out_file to file
os.rename(file + ".tmp", file)
def print_sorted_includes(includes, file=sys.stdout):
default_group_key = 1
groups = collections.defaultdict(set)
# define the groups that we separate correctly. The matchers are tested in the order
# of their priority field. The first matcher that matches the include is used to
# assign the include to a group.
# The groups are printed in the order of their group_key.
matchers = [
{
"name": "system includes",
"matcher": lambda x: x.startswith("<"),
"group_key": -2,
"priority": 0,
},
{
"name": "toplevel postgres includes",
"matcher": lambda x: "/" not in x,
"group_key": 0,
"priority": 9,
},
{
"name": "postgres.h",
"matcher": lambda x: x.strip() in ['"postgres.h"'],
"group_key": -1,
"priority": -1,
},
{
"name": "toplevel citus inlcudes",
"matcher": lambda x: x.strip()
in [
'"citus_version.h"',
'"pg_version_compat.h"',
'"pg_version_constants.h"',
],
"group_key": 3,
"priority": 0,
},
{
"name": "columnar includes",
"matcher": lambda x: x.startswith('"columnar/'),
"group_key": 4,
"priority": 1,
},
{
"name": "distributed includes",
"matcher": lambda x: x.startswith('"distributed/'),
"group_key": 5,
"priority": 1,
},
]
matchers.sort(key=lambda x: x["priority"])
# throughout our codebase we have some includes where either postgres or citus
# includes are wrongfully included with the syntax for system includes. Before we
# try to match those we will change the <> to "" to make them match our system. This
# will also rewrite the include to the correct syntax.
common_system_include_error_prefixes = ["<nodes/", "<distributed/"]
# assign every include to a group
for include in includes:
# extract the group key from the include
include_content = include.split(" ")[1]
# fix common system includes which are secretly postgres or citus includes
for common_prefix in common_system_include_error_prefixes:
if include_content.startswith(common_prefix):
include_content = '"' + include_content.strip()[1:-1] + '"'
include = include.split(" ")[0] + " " + include_content + "\n"
break
group_key = default_group_key
for matcher in matchers:
if matcher["matcher"](include_content):
group_key = matcher["group_key"]
break
groups[group_key].add(include)
# iterate over all groups in the natural order of its keys
for i, group in enumerate(sorted(groups.items())):
if i > 0:
print(file=file)
includes = group[1]
print("".join(sorted(includes)), end="", file=file)
if __name__ == "__main__":
main(sys.argv)

View File

@ -1,12 +0,0 @@
#!/bin/bash
set -euo pipefail
# shellcheck disable=SC1091
source ci/ci_helpers.sh
git ls-files \
| git check-attr --stdin citus-style \
| grep 'citus-style: set' \
| awk '{print $1}' \
| cut -d':' -f1 \
| xargs -n1 ./ci/include_grouping.py

View File

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 94 KiB

View File

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

20
configure vendored
View File

@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.69 for Citus 13.2devel.
# Generated by GNU Autoconf 2.69 for Citus 11.3.0.
#
#
# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
@ -579,8 +579,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='Citus'
PACKAGE_TARNAME='citus'
PACKAGE_VERSION='13.2devel'
PACKAGE_STRING='Citus 13.2devel'
PACKAGE_VERSION='11.3.0'
PACKAGE_STRING='Citus 11.3.0'
PACKAGE_BUGREPORT=''
PACKAGE_URL=''
@ -1262,7 +1262,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
\`configure' configures Citus 13.2devel to adapt to many kinds of systems.
\`configure' configures Citus 11.3.0 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@ -1324,7 +1324,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of Citus 13.2devel:";;
short | recursive ) echo "Configuration of Citus 11.3.0:";;
esac
cat <<\_ACEOF
@ -1429,7 +1429,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
Citus configure 13.2devel
Citus configure 11.3.0
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@ -1912,7 +1912,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
It was created by Citus $as_me 13.2devel, which was
It was created by Citus $as_me 11.3.0, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@ -2588,7 +2588,7 @@ fi
if test "$with_pg_version_check" = no; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: building against PostgreSQL $version_num (skipped compatibility check)" >&5
$as_echo "$as_me: building against PostgreSQL $version_num (skipped compatibility check)" >&6;}
elif test "$version_num" != '15' -a "$version_num" != '16' -a "$version_num" != '17'; then
elif test "$version_num" != '13' -a "$version_num" != '14' -a "$version_num" != '15'; then
as_fn_error $? "Citus is not compatible with the detected PostgreSQL version ${version_num}." "$LINENO" 5
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: building against PostgreSQL $version_num" >&5
@ -5393,7 +5393,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
This file was extended by Citus $as_me 13.2devel, which was
This file was extended by Citus $as_me 11.3.0, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@ -5455,7 +5455,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
Citus config.status 13.2devel
Citus config.status 11.3.0
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"

View File

@ -5,7 +5,7 @@
# everyone needing autoconf installed, the resulting files are checked
# into the SCM.
AC_INIT([Citus], [13.2devel])
AC_INIT([Citus], [11.3.0])
AC_COPYRIGHT([Copyright (c) Citus Data, Inc.])
# we'll need sed and awk for some of the version commands
@ -80,7 +80,7 @@ AC_SUBST(with_pg_version_check)
if test "$with_pg_version_check" = no; then
AC_MSG_NOTICE([building against PostgreSQL $version_num (skipped compatibility check)])
elif test "$version_num" != '15' -a "$version_num" != '16' -a "$version_num" != '17'; then
elif test "$version_num" != '13' -a "$version_num" != '14' -a "$version_num" != '15'; then
AC_MSG_ERROR([Citus is not compatible with the detected PostgreSQL version ${version_num}.])
else
AC_MSG_NOTICE([building against PostgreSQL $version_num])

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 168 KiB

View File

@ -1,6 +1,6 @@
# Columnar extension
comment = 'Citus Columnar extension'
default_version = '12.2-1'
default_version = '11.3-1'
module_pathname = '$libdir/citus_columnar'
relocatable = false
schema = pg_catalog

View File

@ -11,18 +11,16 @@
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include <sys/stat.h>
#include <unistd.h>
#include "postgres.h"
#include "miscadmin.h"
#include "utils/guc.h"
#include "utils/rel.h"
#include "citus_version.h"
#include "columnar/columnar.h"
#include "columnar/columnar_tableam.h"

View File

@ -13,22 +13,16 @@
*/
#include "postgres.h"
#include "citus_version.h"
#include "common/pg_lzcompress.h"
#include "lib/stringinfo.h"
#include "citus_version.h"
#include "pg_version_constants.h"
#include "columnar/columnar_compression.h"
#if HAVE_CITUS_LIBLZ4
#include <lz4.h>
#endif
#if PG_VERSION_NUM >= PG_VERSION_16
#include "varatt.h"
#endif
#if HAVE_LIBZSTD
#include <zstd.h>
#endif

View File

@ -10,17 +10,18 @@
*-------------------------------------------------------------------------
*/
#include <math.h>
#include "citus_version.h"
#include "postgres.h"
#include "miscadmin.h"
#include <math.h>
#include "access/amapi.h"
#include "access/skey.h"
#include "catalog/pg_am.h"
#include "catalog/pg_statistic.h"
#include "commands/defrem.h"
#include "miscadmin.h"
#include "nodes/extensible.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
@ -32,10 +33,6 @@
#include "optimizer/paths.h"
#include "optimizer/plancat.h"
#include "optimizer/restrictinfo.h"
#if PG_VERSION_NUM >= PG_VERSION_16
#include "parser/parse_relation.h"
#include "parser/parsetree.h"
#endif
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/relcache.h"
@ -43,13 +40,10 @@
#include "utils/selfuncs.h"
#include "utils/spccache.h"
#include "citus_version.h"
#include "columnar/columnar.h"
#include "columnar/columnar_customscan.h"
#include "columnar/columnar_metadata.h"
#include "columnar/columnar_tableam.h"
#include "distributed/listutils.h"
/*
@ -133,9 +127,6 @@ static List * set_deparse_context_planstate(List *dpcontext, Node *node,
/* other helpers */
static List * ColumnarVarNeeded(ColumnarScanState *columnarScanState);
static Bitmapset * ColumnarAttrNeeded(ScanState *ss);
#if PG_VERSION_NUM >= PG_VERSION_16
static Bitmapset * fixup_inherited_columns(Oid parentId, Oid childId, Bitmapset *columns);
#endif
/* saved hook value in case of unload */
static set_rel_pathlist_hook_type PreviousSetRelPathlistHook = NULL;
@ -207,7 +198,7 @@ columnar_customscan_init()
&EnableColumnarCustomScan,
true,
PGC_USERSET,
GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,
GUC_NO_SHOW_ALL,
NULL, NULL, NULL);
DefineCustomBoolVariable(
"columnar.enable_qual_pushdown",
@ -217,7 +208,7 @@ columnar_customscan_init()
&EnableColumnarQualPushdown,
true,
PGC_USERSET,
GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,
GUC_NO_SHOW_ALL,
NULL, NULL, NULL);
DefineCustomRealVariable(
"columnar.qual_pushdown_correlation_threshold",
@ -231,7 +222,7 @@ columnar_customscan_init()
0.0,
1.0,
PGC_USERSET,
GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,
GUC_NO_SHOW_ALL,
NULL, NULL, NULL);
DefineCustomIntVariable(
"columnar.max_custom_scan_paths",
@ -243,7 +234,7 @@ columnar_customscan_init()
1,
1024,
PGC_USERSET,
GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,
GUC_NO_SHOW_ALL,
NULL, NULL, NULL);
DefineCustomEnumVariable(
"columnar.planner_debug_level",
@ -363,7 +354,7 @@ ColumnarGetRelationInfoHook(PlannerInfo *root, Oid relationObjectId,
/* disable index-only scan */
IndexOptInfo *indexOptInfo = NULL;
foreach_declared_ptr(indexOptInfo, rel->indexlist)
foreach_ptr(indexOptInfo, rel->indexlist)
{
memset(indexOptInfo->canreturn, false, indexOptInfo->ncolumns * sizeof(bool));
}
@ -381,7 +372,7 @@ RemovePathsByPredicate(RelOptInfo *rel, PathPredicate removePathPredicate)
List *filteredPathList = NIL;
Path *path = NULL;
foreach_declared_ptr(path, rel->pathlist)
foreach_ptr(path, rel->pathlist)
{
if (!removePathPredicate(path))
{
@ -428,7 +419,7 @@ static void
CostColumnarPaths(PlannerInfo *root, RelOptInfo *rel, Oid relationId)
{
Path *path = NULL;
foreach_declared_ptr(path, rel->pathlist)
foreach_ptr(path, rel->pathlist)
{
if (IsA(path, IndexPath))
{
@ -544,7 +535,7 @@ ColumnarIndexScanAdditionalCost(PlannerInfo *root, RelOptInfo *rel,
* "anti-correlated" (-1) since both help us avoiding from reading the
* same stripe again and again.
*/
double absIndexCorrelation = float_abs(indexCorrelation);
double absIndexCorrelation = Abs(indexCorrelation);
/*
* To estimate the number of stripes that we need to read, we do linear
@ -663,7 +654,7 @@ CheckVarStats(PlannerInfo *root, Var *var, Oid sortop, float4 *absVarCorrelation
* If the Var is not highly correlated, then the chunk's min/max bounds
* will be nearly useless.
*/
if (float_abs(varCorrelation) < ColumnarQualPushdownCorrelationThreshold)
if (Abs(varCorrelation) < ColumnarQualPushdownCorrelationThreshold)
{
if (absVarCorrelation)
{
@ -671,7 +662,7 @@ CheckVarStats(PlannerInfo *root, Var *var, Oid sortop, float4 *absVarCorrelation
* Report absVarCorrelation if caller wants to know why given
* var is rejected.
*/
*absVarCorrelation = float_abs(varCorrelation);
*absVarCorrelation = Abs(varCorrelation);
}
return false;
}
@ -783,7 +774,7 @@ ExtractPushdownClause(PlannerInfo *root, RelOptInfo *rel, Node *node)
List *pushdownableArgs = NIL;
Node *boolExprArg = NULL;
foreach_declared_ptr(boolExprArg, boolExpr->args)
foreach_ptr(boolExprArg, boolExpr->args)
{
Expr *pushdownableArg = ExtractPushdownClause(root, rel,
(Node *) boolExprArg);
@ -1051,15 +1042,6 @@ FindCandidateRelids(PlannerInfo *root, RelOptInfo *rel, List *joinClauses)
candidateRelids = bms_del_members(candidateRelids, rel->relids);
candidateRelids = bms_del_members(candidateRelids, rel->lateral_relids);
/*
* For the relevant PG16 commit requiring this addition:
* postgres/postgres@2489d76
*/
#if PG_VERSION_NUM >= PG_VERSION_16
candidateRelids = bms_del_members(candidateRelids, root->outer_join_rels);
#endif
return candidateRelids;
}
@ -1321,8 +1303,11 @@ AddColumnarScanPath(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte,
cpath->methods = &ColumnarScanPathMethods;
#if (PG_VERSION_NUM >= PG_VERSION_15)
/* necessary to avoid extra Result node in PG15 */
cpath->flags = CUSTOMPATH_SUPPORT_PROJECTION;
#endif
/*
* populate generic path information
@ -1386,43 +1371,7 @@ AddColumnarScanPath(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte,
cpath->custom_private = list_make2(NIL, NIL);
}
int numberOfColumnsRead = 0;
#if PG_VERSION_NUM >= PG_VERSION_16
if (rte->perminfoindex > 0)
{
/*
* If perminfoindex > 0, that means that this relation's permission info
* is directly found in the list of rteperminfos of the Query(root->parse)
* So, all we have to do here is retrieve that info.
*/
RTEPermissionInfo *perminfo = getRTEPermissionInfo(root->parse->rteperminfos,
rte);
numberOfColumnsRead = bms_num_members(perminfo->selectedCols);
}
else
{
/*
* If perminfoindex = 0, that means we are skipping the check for permission info
* for this relation, which means that it's either a partition or an inheritance child.
* In these cases, we need to access the permission info of the top parent of this relation.
* After thorough checking, we found that the index of the top parent pointing to the correct
* range table entry in Query's range tables (root->parse->rtable) is found under
* RelOptInfo rel->top_parent->relid.
* For reference, check expand_partitioned_rtentry and expand_inherited_rtentry PG functions
*/
Assert(rel->top_parent);
RangeTblEntry *parent_rte = rt_fetch(rel->top_parent->relid, root->parse->rtable);
RTEPermissionInfo *perminfo = getRTEPermissionInfo(root->parse->rteperminfos,
parent_rte);
numberOfColumnsRead = bms_num_members(fixup_inherited_columns(perminfo->relid,
rte->relid,
perminfo->
selectedCols));
}
#else
numberOfColumnsRead = bms_num_members(rte->selectedCols);
#endif
int numberOfColumnsRead = bms_num_members(rte->selectedCols);
int numberOfClausesPushed = list_length(allClauses);
CostColumnarScan(root, rel, rte->relid, cpath, numberOfColumnsRead,
@ -1442,69 +1391,6 @@ AddColumnarScanPath(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte,
}
#if PG_VERSION_NUM >= PG_VERSION_16
/*
* fixup_inherited_columns
*
* Exact function Copied from PG16 as it's static.
*
* When user is querying on a table with children, it implicitly accesses
* child tables also. So, we also need to check security label of child
* tables and columns, but there is no guarantee attribute numbers are
* same between the parent and children.
* It returns a bitmapset which contains attribute number of the child
* table based on the given bitmapset of the parent.
*/
static Bitmapset *
fixup_inherited_columns(Oid parentId, Oid childId, Bitmapset *columns)
{
Bitmapset *result = NULL;
/*
* obviously, no need to do anything here
*/
if (parentId == childId)
{
return columns;
}
int index = -1;
while ((index = bms_next_member(columns, index)) >= 0)
{
/* bit numbers are offset by FirstLowInvalidHeapAttributeNumber */
AttrNumber attno = index + FirstLowInvalidHeapAttributeNumber;
/*
* whole-row-reference shall be fixed-up later
*/
if (attno == InvalidAttrNumber)
{
result = bms_add_member(result, index);
continue;
}
char *attname = get_attname(parentId, attno, false);
attno = get_attnum(childId, attname);
if (attno == InvalidAttrNumber)
{
elog(ERROR, "cache lookup failed for attribute %s of relation %u",
attname, childId);
}
result = bms_add_member(result,
attno - FirstLowInvalidHeapAttributeNumber);
pfree(attname);
}
return result;
}
#endif
/*
* CostColumnarScan calculates the cost of scanning the columnar table. The
* cost is estimated by using all stripe metadata to estimate based on the
@ -1549,14 +1435,13 @@ ColumnarPerStripeScanCost(RelOptInfo *rel, Oid relationId, int numberOfColumnsRe
ereport(ERROR, (errmsg("could not open relation with OID %u", relationId)));
}
List *stripeList = StripesForRelfilelocator(RelationPhysicalIdentifier_compat(
relation));
List *stripeList = StripesForRelfilenode(relation->rd_node);
RelationClose(relation);
uint32 maxColumnCount = 0;
uint64 totalStripeSize = 0;
StripeMetadata *stripeMetadata = NULL;
foreach_declared_ptr(stripeMetadata, stripeList)
foreach_ptr(stripeMetadata, stripeList)
{
totalStripeSize += stripeMetadata->dataLength;
maxColumnCount = Max(maxColumnCount, stripeMetadata->columnCount);
@ -1607,8 +1492,7 @@ ColumnarTableStripeCount(Oid relationId)
ereport(ERROR, (errmsg("could not open relation with OID %u", relationId)));
}
List *stripeList = StripesForRelfilelocator(RelationPhysicalIdentifier_compat(
relation));
List *stripeList = StripesForRelfilenode(relation->rd_node);
int stripeCount = list_length(stripeList);
RelationClose(relation);
@ -1930,6 +1814,11 @@ ColumnarScan_EndCustomScan(CustomScanState *node)
*/
TableScanDesc scanDesc = node->ss.ss_currentScanDesc;
/*
* Free the exprcontext
*/
ExecFreeExprContext(&node->ss.ps);
/*
* clean out the tuple table
*/

View File

@ -11,12 +11,12 @@
#include "postgres.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "access/nbtree.h"
#include "access/table.h"
#include "catalog/pg_am.h"
#include "catalog/pg_type.h"
#include "distributed/pg_version_constants.h"
#include "miscadmin.h"
#include "storage/fd.h"
#include "storage/smgr.h"
#include "utils/guc.h"
@ -25,8 +25,6 @@
#include "utils/tuplestore.h"
#include "pg_version_compat.h"
#include "pg_version_constants.h"
#include "columnar/columnar.h"
#include "columnar/columnar_storage.h"
#include "columnar/columnar_version_compat.h"
@ -161,5 +159,5 @@ MemoryContextTotals(MemoryContext context, MemoryContextCounters *counters)
MemoryContextTotals(child, counters);
}
context->methods->stats(context, NULL, NULL, counters, true);
context->methods->stats_compat(context, NULL, NULL, counters, true);
}

View File

@ -19,56 +19,45 @@
*/
#include <sys/stat.h>
#include "postgres.h"
#include "miscadmin.h"
#include "port.h"
#include "safe_lib.h"
#include "citus_version.h"
#include "columnar/columnar.h"
#include "columnar/columnar_storage.h"
#include "columnar/columnar_version_compat.h"
#include "distributed/listutils.h"
#include <sys/stat.h>
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/nbtree.h"
#include "access/xact.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
#include "catalog/namespace.h"
#include "commands/defrem.h"
#include "commands/sequence.h"
#include "commands/trigger.h"
#include "executor/executor.h"
#include "executor/spi.h"
#include "lib/stringinfo.h"
#include "miscadmin.h"
#include "nodes/execnodes.h"
#include "lib/stringinfo.h"
#include "port.h"
#include "storage/fd.h"
#include "storage/lmgr.h"
#include "storage/procarray.h"
#include "storage/smgr.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "citus_version.h"
#include "pg_version_constants.h"
#include "columnar/columnar.h"
#include "columnar/columnar_storage.h"
#include "columnar/columnar_version_compat.h"
#include "distributed/listutils.h"
#if PG_VERSION_NUM >= PG_VERSION_16
#include "parser/parse_relation.h"
#include "storage/relfilelocator.h"
#include "utils/relfilenumbermap.h"
#else
#include "utils/relfilenodemap.h"
#endif
#define COLUMNAR_RELOPTION_NAMESPACE "columnar"
#define SLOW_METADATA_ACCESS_WARNING \
@ -123,7 +112,7 @@ static Oid ColumnarChunkGroupRelationId(void);
static Oid ColumnarChunkIndexRelationId(void);
static Oid ColumnarChunkGroupIndexRelationId(void);
static Oid ColumnarNamespaceId(void);
static uint64 LookupStorageId(RelFileLocator relfilelocator);
static uint64 LookupStorageId(RelFileNode relfilenode);
static uint64 GetHighestUsedRowNumber(uint64 storageId);
static void DeleteStorageFromColumnarMetadataTable(Oid metadataTableId,
AttrNumber storageIdAtrrNumber,
@ -602,15 +591,14 @@ ReadColumnarOptions(Oid regclass, ColumnarOptions *options)
* of columnar.chunk.
*/
void
SaveStripeSkipList(RelFileLocator relfilelocator, uint64 stripe,
StripeSkipList *chunkList,
SaveStripeSkipList(RelFileNode relfilenode, uint64 stripe, StripeSkipList *chunkList,
TupleDesc tupleDescriptor)
{
uint32 columnIndex = 0;
uint32 chunkIndex = 0;
uint32 columnCount = chunkList->columnCount;
uint64 storageId = LookupStorageId(relfilelocator);
uint64 storageId = LookupStorageId(relfilenode);
Oid columnarChunkOid = ColumnarChunkRelationId();
Relation columnarChunk = table_open(columnarChunkOid, RowExclusiveLock);
ModifyState *modifyState = StartModifyRelation(columnarChunk);
@ -669,10 +657,10 @@ SaveStripeSkipList(RelFileLocator relfilelocator, uint64 stripe,
* SaveChunkGroups saves the metadata for given chunk groups in columnar.chunk_group.
*/
void
SaveChunkGroups(RelFileLocator relfilelocator, uint64 stripe,
SaveChunkGroups(RelFileNode relfilenode, uint64 stripe,
List *chunkGroupRowCounts)
{
uint64 storageId = LookupStorageId(relfilelocator);
uint64 storageId = LookupStorageId(relfilenode);
Oid columnarChunkGroupOid = ColumnarChunkGroupRelationId();
Relation columnarChunkGroup = table_open(columnarChunkGroupOid, RowExclusiveLock);
ModifyState *modifyState = StartModifyRelation(columnarChunkGroup);
@ -705,8 +693,7 @@ SaveChunkGroups(RelFileLocator relfilelocator, uint64 stripe,
* ReadStripeSkipList fetches chunk metadata for a given stripe.
*/
StripeSkipList *
ReadStripeSkipList(RelFileLocator relfilelocator, uint64 stripe,
TupleDesc tupleDescriptor,
ReadStripeSkipList(RelFileNode relfilenode, uint64 stripe, TupleDesc tupleDescriptor,
uint32 chunkCount, Snapshot snapshot)
{
int32 columnIndex = 0;
@ -714,15 +701,15 @@ ReadStripeSkipList(RelFileLocator relfilelocator, uint64 stripe,
uint32 columnCount = tupleDescriptor->natts;
ScanKeyData scanKey[2];
uint64 storageId = LookupStorageId(relfilelocator);
uint64 storageId = LookupStorageId(relfilenode);
Oid columnarChunkOid = ColumnarChunkRelationId();
Relation columnarChunk = table_open(columnarChunkOid, AccessShareLock);
ScanKeyInit(&scanKey[0], Anum_columnar_chunk_storageid,
BTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(storageId));
BTEqualStrategyNumber, F_OIDEQ, UInt64GetDatum(storageId));
ScanKeyInit(&scanKey[1], Anum_columnar_chunk_stripe,
BTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(stripe));
BTEqualStrategyNumber, F_OIDEQ, Int32GetDatum(stripe));
Oid indexId = ColumnarChunkIndexRelationId();
bool indexOk = OidIsValid(indexId);
@ -928,7 +915,7 @@ StripeMetadataLookupRowNumber(Relation relation, uint64 rowNumber, Snapshot snap
uint64 storageId = ColumnarStorageGetStorageId(relation, false);
ScanKeyData scanKey[2];
ScanKeyInit(&scanKey[0], Anum_columnar_stripe_storageid,
BTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(storageId));
BTEqualStrategyNumber, F_OIDEQ, Int32GetDatum(storageId));
StrategyNumber strategyNumber = InvalidStrategy;
RegProcedure procedure = InvalidOid;
@ -943,7 +930,7 @@ StripeMetadataLookupRowNumber(Relation relation, uint64 rowNumber, Snapshot snap
procedure = F_INT8GT;
}
ScanKeyInit(&scanKey[1], Anum_columnar_stripe_first_row_number,
strategyNumber, procedure, Int64GetDatum(rowNumber));
strategyNumber, procedure, UInt64GetDatum(rowNumber));
Relation columnarStripes = table_open(ColumnarStripeRelationId(), AccessShareLock);
@ -1094,7 +1081,7 @@ FindStripeWithHighestRowNumber(Relation relation, Snapshot snapshot)
uint64 storageId = ColumnarStorageGetStorageId(relation, false);
ScanKeyData scanKey[1];
ScanKeyInit(&scanKey[0], Anum_columnar_stripe_storageid,
BTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(storageId));
BTEqualStrategyNumber, F_OIDEQ, Int32GetDatum(storageId));
Relation columnarStripes = table_open(ColumnarStripeRelationId(), AccessShareLock);
@ -1156,9 +1143,9 @@ ReadChunkGroupRowCounts(uint64 storageId, uint64 stripe, uint32 chunkGroupCount,
ScanKeyData scanKey[2];
ScanKeyInit(&scanKey[0], Anum_columnar_chunkgroup_storageid,
BTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(storageId));
BTEqualStrategyNumber, F_OIDEQ, UInt64GetDatum(storageId));
ScanKeyInit(&scanKey[1], Anum_columnar_chunkgroup_stripe,
BTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(stripe));
BTEqualStrategyNumber, F_OIDEQ, Int32GetDatum(stripe));
Oid indexId = ColumnarChunkGroupIndexRelationId();
bool indexOk = OidIsValid(indexId);
@ -1248,13 +1235,13 @@ InsertEmptyStripeMetadataRow(uint64 storageId, uint64 stripeId, uint32 columnCou
/*
* StripesForRelfilelocator returns a list of StripeMetadata for stripes
* StripesForRelfilenode returns a list of StripeMetadata for stripes
* of the given relfilenode.
*/
List *
StripesForRelfilelocator(RelFileLocator relfilelocator)
StripesForRelfilenode(RelFileNode relfilenode)
{
uint64 storageId = LookupStorageId(relfilelocator);
uint64 storageId = LookupStorageId(relfilenode);
return ReadDataFileStripeList(storageId, GetTransactionSnapshot());
}
@ -1269,9 +1256,9 @@ StripesForRelfilelocator(RelFileLocator relfilelocator)
* returns 0.
*/
uint64
GetHighestUsedAddress(RelFileLocator relfilelocator)
GetHighestUsedAddress(RelFileNode relfilenode)
{
uint64 storageId = LookupStorageId(relfilelocator);
uint64 storageId = LookupStorageId(relfilenode);
uint64 highestUsedAddress = 0;
uint64 highestUsedId = 0;
@ -1385,9 +1372,9 @@ UpdateStripeMetadataRow(uint64 storageId, uint64 stripeId, bool *update,
ScanKeyData scanKey[2];
ScanKeyInit(&scanKey[0], Anum_columnar_stripe_storageid,
BTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(storageId));
BTEqualStrategyNumber, F_OIDEQ, Int32GetDatum(storageId));
ScanKeyInit(&scanKey[1], Anum_columnar_stripe_stripe,
BTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(stripeId));
BTEqualStrategyNumber, F_OIDEQ, Int32GetDatum(stripeId));
Oid columnarStripesOid = ColumnarStripeRelationId();
@ -1464,7 +1451,7 @@ ReadDataFileStripeList(uint64 storageId, Snapshot snapshot)
HeapTuple heapTuple;
ScanKeyInit(&scanKey[0], Anum_columnar_stripe_storageid,
BTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(storageId));
BTEqualStrategyNumber, F_OIDEQ, Int32GetDatum(storageId));
Oid columnarStripesOid = ColumnarStripeRelationId();
@ -1552,7 +1539,7 @@ BuildStripeMetadata(Relation columnarStripes, HeapTuple heapTuple)
* metadata tables.
*/
void
DeleteMetadataRows(RelFileLocator relfilelocator)
DeleteMetadataRows(RelFileNode relfilenode)
{
/*
* During a restore for binary upgrade, metadata tables and indexes may or
@ -1563,7 +1550,7 @@ DeleteMetadataRows(RelFileLocator relfilelocator)
return;
}
uint64 storageId = LookupStorageId(relfilelocator);
uint64 storageId = LookupStorageId(relfilenode);
DeleteStorageFromColumnarMetadataTable(ColumnarStripeRelationId(),
Anum_columnar_stripe_storageid,
@ -1591,7 +1578,7 @@ DeleteStorageFromColumnarMetadataTable(Oid metadataTableId,
{
ScanKeyData scanKey[1];
ScanKeyInit(&scanKey[0], storageIdAtrrNumber, BTEqualStrategyNumber,
F_INT8EQ, Int64GetDatum(storageId));
F_INT8EQ, UInt64GetDatum(storageId));
Relation metadataTable = try_relation_open(metadataTableId, AccessShareLock);
if (metadataTable == NULL)
@ -1636,8 +1623,12 @@ StartModifyRelation(Relation rel)
{
EState *estate = create_estate_for_relation(rel);
#if PG_VERSION_NUM >= PG_VERSION_14
ResultRelInfo *resultRelInfo = makeNode(ResultRelInfo);
InitResultRelInfo(resultRelInfo, rel, 1, NULL, 0);
#else
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
#endif
/* ExecSimpleRelationInsert, ... require caller to open indexes */
ExecOpenIndices(resultRelInfo, false);
@ -1667,7 +1658,7 @@ InsertTupleAndEnforceConstraints(ModifyState *state, Datum *values, bool *nulls)
ExecStoreHeapTuple(tuple, slot, false);
/* use ExecSimpleRelationInsert to enforce constraints */
ExecSimpleRelationInsert(state->resultRelInfo, state->estate, slot);
ExecSimpleRelationInsert_compat(state->resultRelInfo, state->estate, slot);
}
@ -1685,7 +1676,7 @@ DeleteTupleAndEnforceConstraints(ModifyState *state, HeapTuple heapTuple)
simple_heap_delete(state->rel, tid);
/* execute AFTER ROW DELETE Triggers to enforce constraints */
ExecARDeleteTriggers(estate, resultRelInfo, tid, NULL, NULL, false);
ExecARDeleteTriggers_compat(estate, resultRelInfo, tid, NULL, NULL, false);
}
@ -1698,8 +1689,12 @@ FinishModifyRelation(ModifyState *state)
ExecCloseIndices(state->resultRelInfo);
AfterTriggerEndQuery(state->estate);
#if PG_VERSION_NUM >= PG_VERSION_14
ExecCloseResultRelations(state->estate);
ExecCloseRangeTableRelations(state->estate);
#else
ExecCleanUpTriggerState(state->estate);
#endif
ExecResetTupleTable(state->estate->es_tupleTable, false);
FreeExecutorState(state->estate);
@ -1726,13 +1721,15 @@ create_estate_for_relation(Relation rel)
rte->relid = RelationGetRelid(rel);
rte->relkind = rel->rd_rel->relkind;
rte->rellockmode = AccessShareLock;
#if PG_VERSION_NUM >= PG_VERSION_16
List *perminfos = NIL;
addRTEPermissionInfo(&perminfos, rte);
ExecInitRangeTable(estate, list_make1(rte), perminfos);
#else
ExecInitRangeTable(estate, list_make1(rte));
#if PG_VERSION_NUM < PG_VERSION_14
ResultRelInfo *resultRelInfo = makeNode(ResultRelInfo);
InitResultRelInfo(resultRelInfo, rel, 1, NULL, 0);
estate->es_result_relations = resultRelInfo;
estate->es_num_result_relations = 1;
estate->es_result_relation_info = resultRelInfo;
#endif
estate->es_output_cid = GetCurrentCommandId(true);
@ -1937,11 +1934,10 @@ ColumnarNamespaceId(void)
* false if the relation doesn't have a meta page yet.
*/
static uint64
LookupStorageId(RelFileLocator relfilelocator)
LookupStorageId(RelFileNode relfilenode)
{
Oid relationId = RelidByRelfilenumber(RelationTablespace_compat(relfilelocator),
RelationPhysicalIdentifierNumber_compat(
relfilelocator));
Oid relationId = RelidByRelfilenode(relfilenode.spcNode,
relfilenode.relNode);
Relation relation = relation_open(relationId, AccessShareLock);
uint64 storageId = ColumnarStorageGetStorageId(relation, false);
@ -1972,7 +1968,7 @@ columnar_relation_storageid(PG_FUNCTION_ARGS)
Oid relationId = PG_GETARG_OID(0);
Relation relation = relation_open(relationId, AccessShareLock);
if (!object_ownercheck(RelationRelationId, relationId, GetUserId()))
if (!pg_class_ownercheck(relationId, GetUserId()))
{
aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_TABLE,
get_rel_name(relationId));
@ -2041,7 +2037,7 @@ GetHighestUsedRowNumber(uint64 storageId)
List *stripeMetadataList = ReadDataFileStripeList(storageId,
GetTransactionSnapshot());
StripeMetadata *stripeMetadata = NULL;
foreach_declared_ptr(stripeMetadata, stripeMetadataList)
foreach_ptr(stripeMetadata, stripeMetadataList)
{
highestRowNumber = Max(highestRowNumber,
StripeGetHighestRowNumber(stripeMetadata));

View File

@ -22,15 +22,16 @@
#include "access/xact.h"
#include "catalog/pg_am.h"
#include "commands/defrem.h"
#include "distributed/listutils.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/optimizer.h"
#include "optimizer/clauses.h"
#include "optimizer/restrictinfo.h"
#include "storage/fd.h"
#include "utils/guc.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "columnar/columnar.h"
@ -38,8 +39,6 @@
#include "columnar/columnar_tableam.h"
#include "columnar/columnar_version_compat.h"
#include "distributed/listutils.h"
#define UNEXPECTED_STRIPE_READ_ERR_MSG \
"attempted to read an unexpected stripe while reading columnar " \
"table %s, stripe with id=" UINT64_FORMAT " is not flushed"
@ -255,9 +254,8 @@ ColumnarReadFlushPendingWrites(ColumnarReadState *readState)
{
Assert(!readState->snapshotRegisteredByUs);
RelFileNumber relfilenumber = RelationPhysicalIdentifierNumber_compat(
RelationPhysicalIdentifier_compat(readState->relation));
FlushWriteStateForRelfilenumber(relfilenumber, GetCurrentSubTransactionId());
Oid relfilenode = readState->relation->rd_node.relNode;
FlushWriteStateForRelfilenode(relfilenode, GetCurrentSubTransactionId());
if (readState->snapshot == InvalidSnapshot || !IsMVCCSnapshot(readState->snapshot))
{
@ -880,7 +878,7 @@ ReadChunkGroupNextRow(ChunkGroupReadState *chunkGroupReadState, Datum *columnVal
memset(columnNulls, true, sizeof(bool) * chunkGroupReadState->columnCount);
int attno;
foreach_declared_int(attno, chunkGroupReadState->projectedColumnList)
foreach_int(attno, chunkGroupReadState->projectedColumnList)
{
const ChunkData *chunkGroupData = chunkGroupReadState->chunkGroupData;
const int rowIndex = chunkGroupReadState->currentRow;
@ -986,8 +984,7 @@ ColumnarTableRowCount(Relation relation)
{
ListCell *stripeMetadataCell = NULL;
uint64 totalRowCount = 0;
List *stripeList = StripesForRelfilelocator(RelationPhysicalIdentifier_compat(
relation));
List *stripeList = StripesForRelfilenode(relation->rd_node);
foreach(stripeMetadataCell, stripeList)
{
@ -1015,8 +1012,7 @@ LoadFilteredStripeBuffers(Relation relation, StripeMetadata *stripeMetadata,
bool *projectedColumnMask = ProjectedColumnMask(columnCount, projectedColumnList);
StripeSkipList *stripeSkipList = ReadStripeSkipList(RelationPhysicalIdentifier_compat(
relation),
StripeSkipList *stripeSkipList = ReadStripeSkipList(relation->rd_node,
stripeMetadata->id,
tupleDescriptor,
stripeMetadata->chunkCount,
@ -1489,7 +1485,7 @@ ProjectedColumnMask(uint32 columnCount, List *projectedColumnList)
bool *projectedColumnMask = palloc0(columnCount * sizeof(bool));
int attno;
foreach_declared_int(attno, projectedColumnList)
foreach_int(attno, projectedColumnList)
{
/* attno is 1-indexed; projectedColumnMask is 0-indexed */
int columnIndex = attno - 1;
@ -1561,7 +1557,7 @@ DeserializeDatumArray(StringInfo datumBuffer, bool *existsArray, uint32 datumCou
datumTypeLength);
currentDatumDataOffset = att_addlength_datum(currentDatumDataOffset,
datumTypeLength,
datumArray[datumIndex]);
currentDatumDataPointer);
currentDatumDataOffset = att_align_nominal(currentDatumDataOffset,
datumTypeAlign);

View File

@ -36,11 +36,11 @@
#include "postgres.h"
#include "miscadmin.h"
#include "safe_lib.h"
#include "access/generic_xlog.h"
#include "catalog/storage.h"
#include "miscadmin.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
@ -169,11 +169,7 @@ ColumnarStorageInit(SMgrRelation srel, uint64 storageId)
}
/* create two pages */
#if PG_VERSION_NUM >= PG_VERSION_16
PGIOAlignedBlock block;
#else
PGAlignedBlock block;
#endif
Page page = block.data;
/* write metapage */
@ -192,7 +188,7 @@ ColumnarStorageInit(SMgrRelation srel, uint64 storageId)
(char *) &metapage, sizeof(ColumnarMetapage));
phdr->pd_lower += sizeof(ColumnarMetapage);
log_newpage(RelationPhysicalIdentifierBackend_compat(&srel), MAIN_FORKNUM,
log_newpage(&srel->smgr_rnode.node, MAIN_FORKNUM,
COLUMNAR_METAPAGE_BLOCKNO, page, true);
PageSetChecksumInplace(page, COLUMNAR_METAPAGE_BLOCKNO);
smgrextend(srel, MAIN_FORKNUM, COLUMNAR_METAPAGE_BLOCKNO, page, true);
@ -200,7 +196,7 @@ ColumnarStorageInit(SMgrRelation srel, uint64 storageId)
/* write empty page */
PageInit(page, BLCKSZ, 0);
log_newpage(RelationPhysicalIdentifierBackend_compat(&srel), MAIN_FORKNUM,
log_newpage(&srel->smgr_rnode.node, MAIN_FORKNUM,
COLUMNAR_EMPTY_BLOCKNO, page, true);
PageSetChecksumInplace(page, COLUMNAR_EMPTY_BLOCKNO);
smgrextend(srel, MAIN_FORKNUM, COLUMNAR_EMPTY_BLOCKNO, page, true);

View File

@ -1,38 +1,41 @@
#include <math.h>
#include "citus_version.h"
#include "postgres.h"
#include "miscadmin.h"
#include "pgstat.h"
#include "safe_lib.h"
#include <math.h>
#include "miscadmin.h"
#include "access/detoast.h"
#include "access/genam.h"
#include "access/heapam.h"
#include "access/multixact.h"
#include "access/rewriteheap.h"
#include "access/tableam.h"
#include "access/tsmapi.h"
#include "access/detoast.h"
#include "access/xact.h"
#include "catalog/catalog.h"
#include "catalog/index.h"
#include "catalog/namespace.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_am.h"
#include "catalog/pg_extension.h"
#include "catalog/pg_publication.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_extension.h"
#include "catalog/storage.h"
#include "catalog/storage_xlog.h"
#include "commands/defrem.h"
#include "commands/extension.h"
#include "commands/progress.h"
#include "commands/vacuum.h"
#include "commands/extension.h"
#include "executor/executor.h"
#include "nodes/makefuncs.h"
#include "optimizer/plancat.h"
#include "pgstat.h"
#include "safe_lib.h"
#include "storage/bufmgr.h"
#include "storage/bufpage.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "storage/predicate.h"
#include "storage/procarray.h"
@ -40,22 +43,17 @@
#include "tcop/utility.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/pg_rusage.h"
#include "utils/rel.h"
#include "utils/relcache.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
#include "citus_version.h"
#include "pg_version_compat.h"
#include "columnar/columnar.h"
#include "columnar/columnar_customscan.h"
#include "columnar/columnar_storage.h"
#include "columnar/columnar_tableam.h"
#include "columnar/columnar_version_compat.h"
#include "distributed/listutils.h"
/*
@ -117,7 +115,9 @@ static RangeVar * ColumnarProcessAlterTable(AlterTableStmt *alterTableStmt,
List **columnarOptions);
static void ColumnarProcessUtility(PlannedStmt *pstmt,
const char *queryString,
#if PG_VERSION_NUM >= PG_VERSION_14
bool readOnlyTree,
#endif
ProcessUtilityContext context,
ParamListInfo params,
struct QueryEnvironment *queryEnv,
@ -208,8 +208,7 @@ columnar_beginscan_extended(Relation relation, Snapshot snapshot,
uint32 flags, Bitmapset *attr_needed, List *scanQual)
{
CheckCitusColumnarVersion(ERROR);
RelFileNumber relfilenumber = RelationPhysicalIdentifierNumber_compat(
RelationPhysicalIdentifier_compat(relation));
Oid relfilenode = relation->rd_node.relNode;
/*
* A memory context to use for scan-wide data, including the lazily
@ -239,7 +238,7 @@ columnar_beginscan_extended(Relation relation, Snapshot snapshot,
scan->scanQual = copyObject(scanQual);
scan->scanContext = scanContext;
if (PendingWritesInUpperTransactions(relfilenumber, GetCurrentSubTransactionId()))
if (PendingWritesInUpperTransactions(relfilenode, GetCurrentSubTransactionId()))
{
elog(ERROR,
"cannot read from table when there is unflushed data in upper transactions");
@ -435,9 +434,8 @@ columnar_index_fetch_begin(Relation rel)
{
CheckCitusColumnarVersion(ERROR);
RelFileNumber relfilenumber = RelationPhysicalIdentifierNumber_compat(
RelationPhysicalIdentifier_compat(rel));
if (PendingWritesInUpperTransactions(relfilenumber, GetCurrentSubTransactionId()))
Oid relfilenode = rel->rd_node.relNode;
if (PendingWritesInUpperTransactions(relfilenode, GetCurrentSubTransactionId()))
{
/* XXX: maybe we can just flush the data and continue */
elog(ERROR, "cannot read from index when there is unflushed data in "
@ -667,6 +665,7 @@ columnar_tuple_satisfies_snapshot(Relation rel, TupleTableSlot *slot,
}
#if PG_VERSION_NUM >= PG_VERSION_14
static TransactionId
columnar_index_delete_tuples(Relation rel,
TM_IndexDeleteOp *delstate)
@ -715,6 +714,19 @@ columnar_index_delete_tuples(Relation rel,
}
#else
static TransactionId
columnar_compute_xid_horizon_for_tuples(Relation rel,
ItemPointerData *tids,
int nitems)
{
elog(ERROR, "columnar_compute_xid_horizon_for_tuples not implemented");
}
#endif
static void
columnar_tuple_insert(Relation relation, TupleTableSlot *slot, CommandId cid,
int options, BulkInsertState bistate)
@ -819,7 +831,7 @@ static TM_Result
columnar_tuple_update(Relation relation, ItemPointer otid, TupleTableSlot *slot,
CommandId cid, Snapshot snapshot, Snapshot crosscheck,
bool wait, TM_FailureData *tmfd,
LockTupleMode *lockmode, TU_UpdateIndexes *update_indexes)
LockTupleMode *lockmode, bool *update_indexes)
{
elog(ERROR, "columnar_tuple_update not implemented");
}
@ -845,11 +857,11 @@ columnar_finish_bulk_insert(Relation relation, int options)
static void
columnar_relation_set_new_filelocator(Relation rel,
const RelFileLocator *newrlocator,
char persistence,
TransactionId *freezeXid,
MultiXactId *minmulti)
columnar_relation_set_new_filenode(Relation rel,
const RelFileNode *newrnode,
char persistence,
TransactionId *freezeXid,
MultiXactId *minmulti)
{
CheckCitusColumnarVersion(ERROR);
@ -865,19 +877,16 @@ columnar_relation_set_new_filelocator(Relation rel,
* state. If they are equal, this is a new relation object and we don't
* need to clean anything.
*/
if (RelationPhysicalIdentifierNumber_compat(RelationPhysicalIdentifier_compat(rel)) !=
RelationPhysicalIdentifierNumberPtr_compat(newrlocator))
if (rel->rd_node.relNode != newrnode->relNode)
{
MarkRelfilenumberDropped(RelationPhysicalIdentifierNumber_compat(
RelationPhysicalIdentifier_compat(rel)),
GetCurrentSubTransactionId());
MarkRelfilenodeDropped(rel->rd_node.relNode, GetCurrentSubTransactionId());
DeleteMetadataRows(RelationPhysicalIdentifier_compat(rel));
DeleteMetadataRows(rel->rd_node);
}
*freezeXid = RecentXmin;
*minmulti = GetOldestMultiXactId();
SMgrRelation srel = RelationCreateStorage(*newrlocator, persistence, true);
SMgrRelation srel = RelationCreateStorage_compat(*newrnode, persistence, true);
ColumnarStorageInit(srel, ColumnarMetadataNewStorageId());
InitColumnarOptions(rel->rd_id);
@ -892,12 +901,12 @@ static void
columnar_relation_nontransactional_truncate(Relation rel)
{
CheckCitusColumnarVersion(ERROR);
RelFileLocator relfilelocator = RelationPhysicalIdentifier_compat(rel);
RelFileNode relfilenode = rel->rd_node;
NonTransactionDropWriteState(RelationPhysicalIdentifierNumber_compat(relfilelocator));
NonTransactionDropWriteState(relfilenode.relNode);
/* Delete old relfilenode metadata */
DeleteMetadataRows(relfilelocator);
DeleteMetadataRows(relfilenode);
/*
* No need to set new relfilenode, since the table was created in this
@ -914,7 +923,7 @@ columnar_relation_nontransactional_truncate(Relation rel)
static void
columnar_relation_copy_data(Relation rel, const RelFileLocator *newrnode)
columnar_relation_copy_data(Relation rel, const RelFileNode *newrnode)
{
elog(ERROR, "columnar_relation_copy_data not implemented");
}
@ -960,8 +969,7 @@ columnar_relation_copy_for_cluster(Relation OldHeap, Relation NewHeap,
ColumnarOptions columnarOptions = { 0 };
ReadColumnarOptions(OldHeap->rd_id, &columnarOptions);
ColumnarWriteState *writeState = ColumnarBeginWrite(RelationPhysicalIdentifier_compat(
NewHeap),
ColumnarWriteState *writeState = ColumnarBeginWrite(NewHeap->rd_node,
columnarOptions,
targetDesc);
@ -1036,8 +1044,7 @@ NeededColumnsList(TupleDesc tupdesc, Bitmapset *attr_needed)
static uint64
ColumnarTableTupleCount(Relation relation)
{
List *stripeList = StripesForRelfilelocator(RelationPhysicalIdentifier_compat(
relation));
List *stripeList = StripesForRelfilenode(relation->rd_node);
uint64 tupleCount = 0;
ListCell *lc = NULL;
@ -1100,38 +1107,12 @@ columnar_vacuum_rel(Relation rel, VacuumParams *params,
List *indexList = RelationGetIndexList(rel);
int nindexes = list_length(indexList);
#if PG_VERSION_NUM >= PG_VERSION_16
struct VacuumCutoffs cutoffs;
vacuum_get_cutoffs(rel, params, &cutoffs);
Assert(MultiXactIdPrecedesOrEquals(cutoffs.MultiXactCutoff, cutoffs.OldestMxact));
Assert(TransactionIdPrecedesOrEquals(cutoffs.FreezeLimit, cutoffs.OldestXmin));
/*
* Columnar storage doesn't hold any transaction IDs, so we can always
* just advance to the most aggressive value.
*/
TransactionId newRelFrozenXid = cutoffs.OldestXmin;
MultiXactId newRelminMxid = cutoffs.OldestMxact;
double new_live_tuples = ColumnarTableTupleCount(rel);
/* all visible pages are always 0 */
BlockNumber new_rel_allvisible = 0;
bool frozenxid_updated;
bool minmulti_updated;
vac_update_relstats(rel, new_rel_pages, new_live_tuples,
new_rel_allvisible, nindexes > 0,
newRelFrozenXid, newRelminMxid,
&frozenxid_updated, &minmulti_updated, false);
#else
TransactionId oldestXmin;
TransactionId freezeLimit;
MultiXactId multiXactCutoff;
/* initialize xids */
#if (PG_VERSION_NUM >= PG_VERSION_15) && (PG_VERSION_NUM < PG_VERSION_16)
#if PG_VERSION_NUM >= PG_VERSION_15
MultiXactId oldestMxact;
vacuum_set_xid_limits(rel,
params->freeze_min_age,
@ -1161,7 +1142,7 @@ columnar_vacuum_rel(Relation rel, VacuumParams *params,
* just advance to the most aggressive value.
*/
TransactionId newRelFrozenXid = oldestXmin;
#if (PG_VERSION_NUM >= PG_VERSION_15) && (PG_VERSION_NUM < PG_VERSION_16)
#if PG_VERSION_NUM >= PG_VERSION_15
MultiXactId newRelminMxid = oldestMxact;
#else
MultiXactId newRelminMxid = multiXactCutoff;
@ -1172,7 +1153,7 @@ columnar_vacuum_rel(Relation rel, VacuumParams *params,
/* all visible pages are always 0 */
BlockNumber new_rel_allvisible = 0;
#if (PG_VERSION_NUM >= PG_VERSION_15) && (PG_VERSION_NUM < PG_VERSION_16)
#if PG_VERSION_NUM >= PG_VERSION_15
bool frozenxid_updated;
bool minmulti_updated;
@ -1184,7 +1165,6 @@ columnar_vacuum_rel(Relation rel, VacuumParams *params,
vac_update_relstats(rel, new_rel_pages, new_live_tuples,
new_rel_allvisible, nindexes > 0,
newRelFrozenXid, newRelminMxid, false);
#endif
#endif
pgstat_report_vacuum(RelationGetRelid(rel),
@ -1202,7 +1182,7 @@ static void
LogRelationStats(Relation rel, int elevel)
{
ListCell *stripeMetadataCell = NULL;
RelFileLocator relfilelocator = RelationPhysicalIdentifier_compat(rel);
RelFileNode relfilenode = rel->rd_node;
StringInfo infoBuf = makeStringInfo();
int compressionStats[COMPRESSION_COUNT] = { 0 };
@ -1213,13 +1193,13 @@ LogRelationStats(Relation rel, int elevel)
uint64 droppedChunksWithData = 0;
uint64 totalDecompressedLength = 0;
List *stripeList = StripesForRelfilelocator(relfilelocator);
List *stripeList = StripesForRelfilenode(relfilenode);
int stripeCount = list_length(stripeList);
foreach(stripeMetadataCell, stripeList)
{
StripeMetadata *stripe = lfirst(stripeMetadataCell);
StripeSkipList *skiplist = ReadStripeSkipList(relfilelocator, stripe->id,
StripeSkipList *skiplist = ReadStripeSkipList(relfilenode, stripe->id,
RelationGetDescr(rel),
stripe->chunkCount,
GetTransactionSnapshot());
@ -1355,8 +1335,7 @@ TruncateColumnar(Relation rel, int elevel)
* new stripes be added beyond highestPhysicalAddress while
* we're truncating.
*/
uint64 newDataReservation = Max(GetHighestUsedAddress(
RelationPhysicalIdentifier_compat(rel)) + 1,
uint64 newDataReservation = Max(GetHighestUsedAddress(rel->rd_node) + 1,
ColumnarFirstLogicalOffset);
BlockNumber old_rel_pages = smgrnblocks(RelationGetSmgr(rel), MAIN_FORKNUM);
@ -1424,32 +1403,15 @@ ConditionalLockRelationWithTimeout(Relation rel, LOCKMODE lockMode, int timeout,
static bool
columnar_scan_analyze_next_block(TableScanDesc scan,
#if PG_VERSION_NUM >= PG_VERSION_17
ReadStream *stream)
#else
BlockNumber blockno,
columnar_scan_analyze_next_block(TableScanDesc scan, BlockNumber blockno,
BufferAccessStrategy bstrategy)
#endif
{
/*
* Our access method is not pages based, i.e. tuples are not confined
* to pages boundaries. So not much to do here. We return true anyway
* so acquire_sample_rows() in analyze.c would call our
* columnar_scan_analyze_next_tuple() callback.
* In PG17, we return false in case there is no buffer left, since
* the outer loop changed in acquire_sample_rows(), and it is
* expected for the scan_analyze_next_block function to check whether
* there are any blocks left in the block sampler.
*/
#if PG_VERSION_NUM >= PG_VERSION_17
Buffer buf = read_stream_next_buffer(stream, NULL);
if (!BufferIsValid(buf))
{
return false;
}
ReleaseBuffer(buf);
#endif
return true;
}
@ -1522,7 +1484,8 @@ columnar_index_build_range_scan(Relation columnarRelation,
if (!IsBootstrapProcessingMode() && !indexInfo->ii_Concurrent)
{
/* ignore lazy VACUUM's */
OldestXmin = GetOldestNonRemovableTransactionId(columnarRelation);
OldestXmin = GetOldestNonRemovableTransactionId_compat(columnarRelation,
PROCARRAY_FLAGS_VACUUM);
}
Snapshot snapshot = { 0 };
@ -1850,8 +1813,8 @@ ColumnarReadMissingRowsIntoIndex(TableScanDesc scan, Relation indexRelation,
Relation columnarRelation = scan->rs_rd;
IndexUniqueCheck indexUniqueCheck =
indexInfo->ii_Unique ? UNIQUE_CHECK_YES : UNIQUE_CHECK_NO;
index_insert(indexRelation, indexValues, indexNulls, columnarItemPointer,
columnarRelation, indexUniqueCheck, false, indexInfo);
index_insert_compat(indexRelation, indexValues, indexNulls, columnarItemPointer,
columnarRelation, indexUniqueCheck, false, indexInfo);
validateIndexState->tups_inserted += 1;
}
@ -1880,8 +1843,8 @@ TupleSortSkipSmallerItemPointers(Tuplesortstate *tupleSort, ItemPointer targetIt
Datum *abbrev = NULL;
Datum tsDatum;
bool tsDatumIsNull;
if (!tuplesort_getdatum_compat(tupleSort, forwardDirection, false,
&tsDatum, &tsDatumIsNull, abbrev))
if (!tuplesort_getdatum(tupleSort, forwardDirection, &tsDatum,
&tsDatumIsNull, abbrev))
{
ItemPointerSetInvalid(&tsItemPointerData);
break;
@ -2055,7 +2018,7 @@ columnar_tableam_init()
&EnableVersionChecksColumnar,
true,
PGC_USERSET,
GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,
GUC_NO_SHOW_ALL,
NULL, NULL, NULL);
}
@ -2122,13 +2085,12 @@ ColumnarTableDropHook(Oid relid)
* tableam tables storage is managed by postgres.
*/
Relation rel = table_open(relid, AccessExclusiveLock);
RelFileLocator relfilelocator = RelationPhysicalIdentifier_compat(rel);
RelFileNode relfilenode = rel->rd_node;
DeleteMetadataRows(relfilelocator);
DeleteMetadataRows(relfilenode);
DeleteColumnarTableOptions(rel->rd_id, true);
MarkRelfilenumberDropped(RelationPhysicalIdentifierNumber_compat(relfilelocator),
GetCurrentSubTransactionId());
MarkRelfilenodeDropped(relfilenode.relNode, GetCurrentSubTransactionId());
/* keep the lock since we did physical changes to the relation */
table_close(rel, NoLock);
@ -2245,6 +2207,7 @@ ColumnarProcessAlterTable(AlterTableStmt *alterTableStmt, List **columnarOptions
columnarRangeVar = alterTableStmt->relation;
}
}
#if PG_VERSION_NUM >= PG_VERSION_15
else if (alterTableCmd->subtype == AT_SetAccessMethod)
{
if (columnarRangeVar || *columnarOptions)
@ -2255,15 +2218,14 @@ ColumnarProcessAlterTable(AlterTableStmt *alterTableStmt, List **columnarOptions
"Specify SET ACCESS METHOD before storage parameters, or use separate ALTER TABLE commands.")));
}
destIsColumnar = (strcmp(alterTableCmd->name ? alterTableCmd->name :
default_table_access_method,
COLUMNAR_AM_NAME) == 0);
destIsColumnar = (strcmp(alterTableCmd->name, COLUMNAR_AM_NAME) == 0);
if (srcIsColumnar && !destIsColumnar)
{
DeleteColumnarTableOptions(RelationGetRelid(rel), true);
}
}
#endif /* PG_VERSION_15 */
}
relation_close(rel, NoLock);
@ -2278,17 +2240,21 @@ ColumnarProcessAlterTable(AlterTableStmt *alterTableStmt, List **columnarOptions
static void
ColumnarProcessUtility(PlannedStmt *pstmt,
const char *queryString,
#if PG_VERSION_NUM >= PG_VERSION_14
bool readOnlyTree,
#endif
ProcessUtilityContext context,
ParamListInfo params,
struct QueryEnvironment *queryEnv,
DestReceiver *dest,
QueryCompletion *completionTag)
{
#if PG_VERSION_NUM >= PG_VERSION_14
if (readOnlyTree)
{
pstmt = copyObject(pstmt);
}
#endif
Node *parsetree = pstmt->utilityStmt;
@ -2405,8 +2371,8 @@ ColumnarProcessUtility(PlannedStmt *pstmt,
CheckCitusColumnarAlterExtensionStmt(parsetree);
}
PrevProcessUtilityHook(pstmt, queryString, false, context,
params, queryEnv, dest, completionTag);
PrevProcessUtilityHook_compat(pstmt, queryString, false, context,
params, queryEnv, dest, completionTag);
if (columnarOptions != NIL)
{
@ -2534,7 +2500,11 @@ static const TableAmRoutine columnar_am_methods = {
.tuple_get_latest_tid = columnar_get_latest_tid,
.tuple_tid_valid = columnar_tuple_tid_valid,
.tuple_satisfies_snapshot = columnar_tuple_satisfies_snapshot,
#if PG_VERSION_NUM >= PG_VERSION_14
.index_delete_tuples = columnar_index_delete_tuples,
#else
.compute_xid_horizon_for_tuples = columnar_compute_xid_horizon_for_tuples,
#endif
.tuple_insert = columnar_tuple_insert,
.tuple_insert_speculative = columnar_tuple_insert_speculative,
@ -2545,11 +2515,7 @@ static const TableAmRoutine columnar_am_methods = {
.tuple_lock = columnar_tuple_lock,
.finish_bulk_insert = columnar_finish_bulk_insert,
#if PG_VERSION_NUM >= PG_VERSION_16
.relation_set_new_filelocator = columnar_relation_set_new_filelocator,
#else
.relation_set_new_filenode = columnar_relation_set_new_filelocator,
#endif
.relation_set_new_filenode = columnar_relation_set_new_filenode,
.relation_nontransactional_truncate = columnar_relation_nontransactional_truncate,
.relation_copy_data = columnar_relation_copy_data,
.relation_copy_for_cluster = columnar_relation_copy_for_cluster,
@ -2647,12 +2613,21 @@ ColumnarCheckLogicalReplication(Relation rel)
return;
}
#if PG_VERSION_NUM >= PG_VERSION_15
{
PublicationDesc pubdesc;
RelationBuildPublicationDesc(rel, &pubdesc);
pubActionInsert = pubdesc.pubactions.pubinsert;
}
#else
if (rel->rd_pubactions == NULL)
{
GetRelationPublicationActions(rel);
Assert(rel->rd_pubactions != NULL);
}
pubActionInsert = rel->rd_pubactions->pubinsert;
#endif
if (pubActionInsert)
{
@ -2954,7 +2929,7 @@ MajorVersionsCompatibleColumnar(char *leftVersion, char *rightVersion)
}
else
{
rightComparisionLimit = strlen(rightVersion);
rightComparisionLimit = strlen(leftVersion);
}
/* we can error out early if hypens are not in the same position */
@ -3029,8 +3004,6 @@ AvailableExtensionVersionColumnar(void)
ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("citus extension is not found")));
return NULL; /* keep compiler happy */
}
@ -3093,7 +3066,7 @@ DefElem *
GetExtensionOption(List *extensionOptions, const char *defname)
{
DefElem *defElement = NULL;
foreach_declared_ptr(defElement, extensionOptions)
foreach_ptr(defElement, extensionOptions)
{
if (IsA(defElement, DefElem) &&
strncmp(defElement->defname, defname, NAMEDATALEN) == 0)

View File

@ -16,37 +16,28 @@
#include "postgres.h"
#include "miscadmin.h"
#include "safe_lib.h"
#include "access/heapam.h"
#include "access/nbtree.h"
#include "catalog/pg_am.h"
#include "miscadmin.h"
#include "storage/fd.h"
#include "storage/smgr.h"
#include "utils/guc.h"
#include "utils/memutils.h"
#include "utils/rel.h"
#include "pg_version_compat.h"
#include "pg_version_constants.h"
#include "utils/relfilenodemap.h"
#include "columnar/columnar.h"
#include "columnar/columnar_storage.h"
#include "columnar/columnar_version_compat.h"
#if PG_VERSION_NUM >= PG_VERSION_16
#include "storage/relfilelocator.h"
#include "utils/relfilenumbermap.h"
#else
#include "utils/relfilenodemap.h"
#endif
struct ColumnarWriteState
{
TupleDesc tupleDescriptor;
FmgrInfo **comparisonFunctionArray;
RelFileLocator relfilelocator;
RelFileNode relfilenode;
MemoryContext stripeWriteContext;
MemoryContext perTupleContext;
@ -93,7 +84,7 @@ static StringInfo CopyStringInfo(StringInfo sourceString);
* data load operation.
*/
ColumnarWriteState *
ColumnarBeginWrite(RelFileLocator relfilelocator,
ColumnarBeginWrite(RelFileNode relfilenode,
ColumnarOptions options,
TupleDesc tupleDescriptor)
{
@ -133,7 +124,7 @@ ColumnarBeginWrite(RelFileLocator relfilelocator,
options.chunkRowCount);
ColumnarWriteState *writeState = palloc0(sizeof(ColumnarWriteState));
writeState->relfilelocator = relfilelocator;
writeState->relfilenode = relfilenode;
writeState->options = options;
writeState->tupleDescriptor = CreateTupleDescCopy(tupleDescriptor);
writeState->comparisonFunctionArray = comparisonFunctionArray;
@ -183,10 +174,8 @@ ColumnarWriteRow(ColumnarWriteState *writeState, Datum *columnValues, bool *colu
writeState->stripeSkipList = stripeSkipList;
writeState->compressionBuffer = makeStringInfo();
Oid relationId = RelidByRelfilenumber(RelationTablespace_compat(
writeState->relfilelocator),
RelationPhysicalIdentifierNumber_compat(
writeState->relfilelocator));
Oid relationId = RelidByRelfilenode(writeState->relfilenode.spcNode,
writeState->relfilenode.relNode);
Relation relation = relation_open(relationId, NoLock);
writeState->emptyStripeReservation =
ReserveEmptyStripe(relation, columnCount, chunkRowCount,
@ -404,10 +393,8 @@ FlushStripe(ColumnarWriteState *writeState)
elog(DEBUG1, "Flushing Stripe of size %d", stripeBuffers->rowCount);
Oid relationId = RelidByRelfilenumber(RelationTablespace_compat(
writeState->relfilelocator),
RelationPhysicalIdentifierNumber_compat(
writeState->relfilelocator));
Oid relationId = RelidByRelfilenode(writeState->relfilenode.spcNode,
writeState->relfilenode.relNode);
Relation relation = relation_open(relationId, NoLock);
/*
@ -499,10 +486,10 @@ FlushStripe(ColumnarWriteState *writeState)
}
}
SaveChunkGroups(writeState->relfilelocator,
SaveChunkGroups(writeState->relfilenode,
stripeMetadata->id,
writeState->chunkGroupRowCounts);
SaveStripeSkipList(writeState->relfilelocator,
SaveStripeSkipList(writeState->relfilenode,
stripeMetadata->id,
stripeSkipList, tupleDescriptor);

View File

@ -1 +0,0 @@
-- citus_columnar--11.3-1--12.2-1

View File

@ -1 +0,0 @@
-- citus_columnar--12.2-1--11.3-1

View File

@ -1,17 +1,21 @@
#include "citus_version.h"
#include "postgres.h"
#include "columnar/columnar.h"
#include <math.h>
#include "postgres.h"
#include "miscadmin.h"
#include "pgstat.h"
#include "access/genam.h"
#include "access/heapam.h"
#include "access/heaptoast.h"
#include "access/multixact.h"
#include "access/rewriteheap.h"
#include "access/tsmapi.h"
#include "access/heaptoast.h"
#include "common/hashfn.h"
#include "access/xact.h"
#include "catalog/catalog.h"
#include "catalog/index.h"
@ -22,12 +26,13 @@
#include "catalog/storage_xlog.h"
#include "commands/progress.h"
#include "commands/vacuum.h"
#include "common/hashfn.h"
#include "executor/executor.h"
#include "nodes/makefuncs.h"
#include "optimizer/plancat.h"
#include "pgstat.h"
#include "storage/bufmgr.h"
#include "storage/bufpage.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "storage/predicate.h"
#include "storage/procarray.h"
@ -38,10 +43,6 @@
#include "utils/rel.h"
#include "utils/syscache.h"
#include "citus_version.h"
#include "pg_version_compat.h"
#include "columnar/columnar.h"
#include "columnar/columnar_customscan.h"
#include "columnar/columnar_tableam.h"
#include "columnar/columnar_version_compat.h"
@ -76,7 +77,7 @@ typedef struct SubXidWriteState
typedef struct WriteStateMapEntry
{
/* key of the entry */
RelFileNumber relfilenumber;
Oid relfilenode;
/*
* If a table is dropped, we set dropped to true and set dropSubXid to the
@ -131,7 +132,7 @@ columnar_init_write_state(Relation relation, TupleDesc tupdesc,
HASHCTL info;
uint32 hashFlags = (HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
memset(&info, 0, sizeof(info));
info.keysize = sizeof(RelFileNumber);
info.keysize = sizeof(Oid);
info.hash = oid_hash;
info.entrysize = sizeof(WriteStateMapEntry);
info.hcxt = WriteStateContext;
@ -145,10 +146,7 @@ columnar_init_write_state(Relation relation, TupleDesc tupdesc,
MemoryContextRegisterResetCallback(WriteStateContext, &cleanupCallback);
}
WriteStateMapEntry *hashEntry = hash_search(WriteStateMap,
&RelationPhysicalIdentifierNumber_compat(
RelationPhysicalIdentifier_compat(
relation)),
WriteStateMapEntry *hashEntry = hash_search(WriteStateMap, &relation->rd_node.relNode,
HASH_ENTER, &found);
if (!found)
{
@ -191,8 +189,7 @@ columnar_init_write_state(Relation relation, TupleDesc tupdesc,
ReadColumnarOptions(tupSlotRelationId, &columnarOptions);
SubXidWriteState *stackEntry = palloc0(sizeof(SubXidWriteState));
stackEntry->writeState = ColumnarBeginWrite(RelationPhysicalIdentifier_compat(
relation),
stackEntry->writeState = ColumnarBeginWrite(relation->rd_node,
columnarOptions,
tupdesc);
stackEntry->subXid = currentSubXid;
@ -209,16 +206,14 @@ columnar_init_write_state(Relation relation, TupleDesc tupdesc,
* Flushes pending writes for given relfilenode in the given subtransaction.
*/
void
FlushWriteStateForRelfilenumber(RelFileNumber relfilenumber,
SubTransactionId currentSubXid)
FlushWriteStateForRelfilenode(Oid relfilenode, SubTransactionId currentSubXid)
{
if (WriteStateMap == NULL)
{
return;
}
WriteStateMapEntry *entry = hash_search(WriteStateMap, &relfilenumber, HASH_FIND,
NULL);
WriteStateMapEntry *entry = hash_search(WriteStateMap, &relfilenode, HASH_FIND, NULL);
Assert(!entry || !entry->dropped);
@ -325,14 +320,14 @@ DiscardWriteStateForAllRels(SubTransactionId currentSubXid, SubTransactionId par
* Called when the given relfilenode is dropped.
*/
void
MarkRelfilenumberDropped(RelFileNumber relfilenumber, SubTransactionId currentSubXid)
MarkRelfilenodeDropped(Oid relfilenode, SubTransactionId currentSubXid)
{
if (WriteStateMap == NULL)
{
return;
}
WriteStateMapEntry *entry = hash_search(WriteStateMap, &relfilenumber, HASH_FIND,
WriteStateMapEntry *entry = hash_search(WriteStateMap, &relfilenode, HASH_FIND,
NULL);
if (!entry || entry->dropped)
{
@ -348,11 +343,11 @@ MarkRelfilenumberDropped(RelFileNumber relfilenumber, SubTransactionId currentSu
* Called when the given relfilenode is dropped in non-transactional TRUNCATE.
*/
void
NonTransactionDropWriteState(RelFileNumber relfilenumber)
NonTransactionDropWriteState(Oid relfilenode)
{
if (WriteStateMap)
{
hash_search(WriteStateMap, &relfilenumber, HASH_REMOVE, false);
hash_search(WriteStateMap, &relfilenode, HASH_REMOVE, false);
}
}
@ -361,16 +356,14 @@ NonTransactionDropWriteState(RelFileNumber relfilenumber)
* Returns true if there are any pending writes in upper transactions.
*/
bool
PendingWritesInUpperTransactions(RelFileNumber relfilenumber,
SubTransactionId currentSubXid)
PendingWritesInUpperTransactions(Oid relfilenode, SubTransactionId currentSubXid)
{
if (WriteStateMap == NULL)
{
return false;
}
WriteStateMapEntry *entry = hash_search(WriteStateMap, &relfilenumber, HASH_FIND,
NULL);
WriteStateMapEntry *entry = hash_search(WriteStateMap, &relfilenode, HASH_FIND, NULL);
if (entry && entry->writeStateStack != NULL)
{

View File

@ -18,7 +18,7 @@ generated_downgrade_sql_files += $(patsubst %,$(citus_abs_srcdir)/build/sql/%,$(
DATA_built = $(generated_sql_files)
# directories with source files
SUBDIRS = . commands connection ddl deparser executor metadata operations planner progress relay safeclib shardsplit stats test transaction utils worker clock
SUBDIRS = . commands connection ddl deparser executor metadata operations planner progress relay safeclib shardsplit test transaction utils worker clock
# enterprise modules
SUBDIRS += replication

File diff suppressed because it is too large Load Diff

View File

@ -14,6 +14,11 @@ override CPPFLAGS += -DDECODER=\"$(DECODER)\" -I$(citus_abs_top_srcdir)/include
install: install-cdc
clean: clean-cdc
install-cdc:
mkdir -p '$(citus_decoders_dir)'
$(INSTALL_SHLIB) citus_$(DECODER)$(DLSUFFIX) '$(citus_decoders_dir)/$(DECODER)$(DLSUFFIX)'
$(INSTALL_SHLIB) citus_$(DECODER).so '$(citus_decoders_dir)/$(DECODER).so'
clean-cdc:
rm -f '$(DESTDIR)$(datadir)/$(datamoduledir)/citus_decoders/$(DECODER).so'

View File

@ -8,9 +8,8 @@
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "cdc_decoder_utils.h"
#include "postgres.h"
#include "fmgr.h"
#include "access/genam.h"
@ -22,8 +21,6 @@
#include "utils/rel.h"
#include "utils/typcache.h"
#include "pg_version_constants.h"
PG_MODULE_MAGIC;
extern void _PG_output_plugin_init(OutputPluginCallbacks *cb);
@ -206,7 +203,8 @@ AddShardIdToHashTable(uint64 shardId, ShardIdHashEntry *entry)
{
entry->shardId = shardId;
entry->distributedTableId = CdcLookupShardRelationFromCatalog(shardId, true);
entry->isReferenceTable = CdcIsReferenceTableViaCatalog(entry->distributedTableId);
entry->isReferenceTable = CdcPartitionMethodViaCatalog(entry->distributedTableId) ==
'n';
return entry->distributedTableId;
}
@ -363,14 +361,12 @@ GetTupleForTargetSchemaForCdc(HeapTuple sourceRelationTuple,
targetNulls[targetIndex] = true;
targetIndex++;
}
/* If this source attribute has been dropped, just skip this source attribute.*/
else if (TupleDescAttr(sourceRelDesc, sourceIndex)->attisdropped)
{
sourceIndex++;
continue;
}
/* If both source and target attributes are not dropped, add the attribute field to targetValues. */
else if (sourceIndex < sourceRelDesc->natts)
{
@ -437,74 +433,6 @@ TranslateChangesIfSchemaChanged(Relation sourceRelation, Relation targetRelation
return;
}
#if PG_VERSION_NUM >= PG_VERSION_17
/* Check the ReorderBufferChange's action type and handle them accordingly.*/
switch (change->action)
{
case REORDER_BUFFER_CHANGE_INSERT:
{
/* For insert action, only new tuple should always be translated*/
HeapTuple sourceRelationNewTuple = change->data.tp.newtuple;
HeapTuple targetRelationNewTuple = GetTupleForTargetSchemaForCdc(
sourceRelationNewTuple, sourceRelationDesc, targetRelationDesc);
change->data.tp.newtuple = targetRelationNewTuple;
break;
}
/*
* For update changes both old and new tuples need to be translated for target relation
* if the REPLICA IDENTITY is set to FULL. Otherwise, only the new tuple needs to be
* translated for target relation.
*/
case REORDER_BUFFER_CHANGE_UPDATE:
{
/* For update action, new tuple should always be translated*/
/* Get the new tuple from the ReorderBufferChange, and translate it to target relation. */
HeapTuple sourceRelationNewTuple = change->data.tp.newtuple;
HeapTuple targetRelationNewTuple = GetTupleForTargetSchemaForCdc(
sourceRelationNewTuple, sourceRelationDesc, targetRelationDesc);
change->data.tp.newtuple = targetRelationNewTuple;
/*
* Format oldtuple according to the target relation. If the column values of replica
* identiy change, then the old tuple is non-null and needs to be formatted according
* to the target relation schema.
*/
if (change->data.tp.oldtuple != NULL)
{
HeapTuple sourceRelationOldTuple = change->data.tp.oldtuple;
HeapTuple targetRelationOldTuple = GetTupleForTargetSchemaForCdc(
sourceRelationOldTuple,
sourceRelationDesc,
targetRelationDesc);
change->data.tp.oldtuple = targetRelationOldTuple;
}
break;
}
case REORDER_BUFFER_CHANGE_DELETE:
{
/* For delete action, only old tuple should be translated*/
HeapTuple sourceRelationOldTuple = change->data.tp.oldtuple;
HeapTuple targetRelationOldTuple = GetTupleForTargetSchemaForCdc(
sourceRelationOldTuple,
sourceRelationDesc,
targetRelationDesc);
change->data.tp.oldtuple = targetRelationOldTuple;
break;
}
default:
{
/* Do nothing for other action types. */
break;
}
}
#else
/* Check the ReorderBufferChange's action type and handle them accordingly.*/
switch (change->action)
{
@ -569,5 +497,4 @@ TranslateChangesIfSchemaChanged(Relation sourceRelation, Relation targetRelation
break;
}
}
#endif
}

View File

@ -8,21 +8,18 @@
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "cdc_decoder_utils.h"
#include "commands/extension.h"
#include "fmgr.h"
#include "miscadmin.h"
#include "access/genam.h"
#include "access/heapam.h"
#include "catalog/pg_namespace.h"
#include "commands/extension.h"
#include "common/hashfn.h"
#include "common/string.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/typcache.h"
#include "utils/lsyscache.h"
#include "catalog/pg_namespace.h"
#include "cdc_decoder_utils.h"
#include "distributed/pg_dist_partition.h"
#include "distributed/pg_dist_shard.h"
#include "distributed/relay_utility.h"
@ -75,7 +72,7 @@ DistShardRelationId(void)
/*
* DistShardShardidIndexId returns the relation id of the pg_dist_shard_shardid_index
* DistShardRelationId returns the relation id of the pg_dist_shard
*/
static Oid
DistShardShardidIndexId(void)
@ -90,7 +87,7 @@ DistShardShardidIndexId(void)
/*
* DistPartitionRelationId returns the relation id of the pg_dist_partition
* DistShardRelationId returns the relation id of the pg_dist_shard
*/
static Oid
DistPartitionRelationId(void)
@ -187,9 +184,9 @@ CdcExtractShardIdFromTableName(const char *tableName, bool missingOk)
/*
* CdcGetLocalGroupId returns the group identifier of the local node. The
* function assumes that pg_dist_local_group has exactly one row and has at
* least one column. Otherwise, the function errors out.
* CdcGetLocalGroupId returns the group identifier of the local node. The function assumes
* that pg_dist_local_node_group has exactly one row and has at least one column.
* Otherwise, the function errors out.
*/
static int32
CdcGetLocalGroupId(void)
@ -334,16 +331,16 @@ CdcPgDistPartitionTupleViaCatalog(Oid relationId)
/*
* CdcIsReferenceTableViaCatalog gets a relationId and returns true if the relation
* is a reference table and false otherwise.
* CdcPartitionMethodViaCatalog gets a relationId and returns the partition
* method column from pg_dist_partition via reading from catalog.
*/
char
CdcIsReferenceTableViaCatalog(Oid relationId)
CdcPartitionMethodViaCatalog(Oid relationId)
{
HeapTuple partitionTuple = CdcPgDistPartitionTupleViaCatalog(relationId);
if (!HeapTupleIsValid(partitionTuple))
{
return false;
return DISTRIBUTE_BY_INVALID;
}
Datum datumArray[Natts_pg_dist_partition];
@ -354,33 +351,21 @@ CdcIsReferenceTableViaCatalog(Oid relationId)
TupleDesc tupleDescriptor = RelationGetDescr(pgDistPartition);
heap_deform_tuple(partitionTuple, tupleDescriptor, datumArray, isNullArray);
if (isNullArray[Anum_pg_dist_partition_partmethod - 1] ||
isNullArray[Anum_pg_dist_partition_repmodel - 1])
if (isNullArray[Anum_pg_dist_partition_partmethod - 1])
{
/*
* partition method and replication model cannot be NULL,
* still let's make sure
*/
/* partition method cannot be NULL, still let's make sure */
heap_freetuple(partitionTuple);
table_close(pgDistPartition, NoLock);
return false;
return DISTRIBUTE_BY_INVALID;
}
Datum partitionMethodDatum = datumArray[Anum_pg_dist_partition_partmethod - 1];
char partitionMethodChar = DatumGetChar(partitionMethodDatum);
Datum replicationModelDatum = datumArray[Anum_pg_dist_partition_repmodel - 1];
char replicationModelChar = DatumGetChar(replicationModelDatum);
heap_freetuple(partitionTuple);
table_close(pgDistPartition, NoLock);
/*
* A table is a reference table when its partition method is 'none'
* and replication model is 'two phase commit'
*/
return partitionMethodChar == DISTRIBUTE_BY_NONE &&
replicationModelChar == REPLICATION_MODEL_2PC;
return partitionMethodChar;
}

View File

@ -12,11 +12,9 @@
#define CITUS_CDC_DECODER_H
#include "postgres.h"
#include "c.h"
#include "fmgr.h"
#include "replication/logical.h"
#include "c.h"
#define InvalidRepOriginId 0
#define INVALID_SHARD_ID 0
@ -27,7 +25,7 @@ uint64 CdcExtractShardIdFromTableName(const char *tableName, bool missingOk);
Oid CdcLookupShardRelationFromCatalog(int64 shardId, bool missingOk);
char CdcIsReferenceTableViaCatalog(Oid relationId);
char CdcPartitionMethodViaCatalog(Oid relationId);
bool CdcCitusHasBeenLoaded(void);

View File

@ -1,6 +1,6 @@
# Citus extension
comment = 'Citus distributed database'
default_version = '13.2-1'
default_version = '11.3-1'
module_pathname = '$libdir/citus'
relocatable = false
schema = pg_catalog

View File

@ -11,37 +11,36 @@
#include <sys/time.h>
#include "postgres.h"
#include "miscadmin.h"
#include "fmgr.h"
#include "funcapi.h"
#include "libpq-fe.h"
#include "miscadmin.h"
#include "catalog/namespace.h"
#include "commands/extension.h"
#include "commands/sequence.h"
#include "executor/spi.h"
#include "nodes/pg_list.h"
#include "postmaster/postmaster.h"
#include "storage/ipc.h"
#include "storage/lwlock.h"
#include "storage/s_lock.h"
#include "storage/shmem.h"
#include "storage/spin.h"
#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/numeric.h"
#include "utils/typcache.h"
#include "nodes/pg_list.h"
#include "catalog/namespace.h"
#include "commands/extension.h"
#include "commands/sequence.h"
#include "executor/spi.h"
#include "postmaster/postmaster.h"
#include "storage/ipc.h"
#include "storage/lwlock.h"
#include "storage/shmem.h"
#include "storage/spin.h"
#include "storage/s_lock.h"
#include "distributed/causal_clock.h"
#include "distributed/citus_safe_lib.h"
#include "distributed/coordinator_protocol.h"
#include "distributed/listutils.h"
#include "distributed/local_executor.h"
#include "distributed/lock_graph.h"
#include "distributed/local_executor.h"
#include "distributed/metadata_cache.h"
#include "distributed/placement_connection.h"
#include "distributed/remote_commands.h"
#include "distributed/placement_connection.h"
#include "distributed/coordinator_protocol.h"
#include "distributed/citus_safe_lib.h"
#define SAVE_AND_PERSIST(c) \
do { \
@ -145,6 +144,17 @@ LogicalClockShmemSize(void)
void
InitializeClusterClockMem(void)
{
/* On PG 15 and above, we use shmem_request_hook_type */
#if PG_VERSION_NUM < PG_VERSION_15
/* allocate shared memory for pre PG-15 versions */
if (!IsUnderPostmaster)
{
RequestAddinShmemSpace(LogicalClockShmemSize());
}
#endif
prev_shmem_startup_hook = shmem_startup_hook;
shmem_startup_hook = LogicalClockShmemInit;
}
@ -317,7 +327,7 @@ GetHighestClockInTransaction(List *nodeConnectionList)
{
MultiConnection *connection = NULL;
foreach_declared_ptr(connection, nodeConnectionList)
foreach_ptr(connection, nodeConnectionList)
{
int querySent =
SendRemoteCommand(connection, "SELECT citus_get_node_clock();");
@ -338,7 +348,7 @@ GetHighestClockInTransaction(List *nodeConnectionList)
globalClockValue->counter)));
/* fetch the results and pick the highest clock value of all the nodes */
foreach_declared_ptr(connection, nodeConnectionList)
foreach_ptr(connection, nodeConnectionList)
{
bool raiseInterrupts = true;
@ -386,7 +396,7 @@ AdjustClocksToTransactionHighest(List *nodeConnectionList,
/* Set the clock value on participating worker nodes */
appendStringInfo(queryToSend,
"SELECT citus_internal.adjust_local_clock_to_remote"
"SELECT pg_catalog.citus_internal_adjust_local_clock_to_remote"
"('(%lu, %u)'::pg_catalog.cluster_clock);",
transactionClockValue->logical, transactionClockValue->counter);
@ -420,11 +430,6 @@ PrepareAndSetTransactionClock(void)
MultiConnection *connection = dlist_container(MultiConnection, transactionNode,
iter.cur);
WorkerNode *workerNode = FindWorkerNode(connection->hostname, connection->port);
if (!workerNode)
{
ereport(WARNING, errmsg("Worker node is missing"));
continue;
}
/* Skip the node if we already in the list */
if (list_member_int(nodeList, workerNode->groupId))

View File

@ -34,16 +34,9 @@
#include "catalog/pg_am.h"
#include "catalog/pg_depend.h"
#include "catalog/pg_rewrite_d.h"
#include "commands/defrem.h"
#include "executor/spi.h"
#include "nodes/pg_list.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
#include "columnar/columnar.h"
#include "columnar/columnar_tableam.h"
#include "commands/defrem.h"
#include "distributed/colocation_utils.h"
#include "distributed/commands.h"
#include "distributed/commands/utility_hook.h"
@ -60,15 +53,18 @@
#include "distributed/multi_executor.h"
#include "distributed/multi_logical_planner.h"
#include "distributed/multi_partitioning_utils.h"
#include "distributed/namespace_utils.h"
#include "distributed/reference_table_utils.h"
#include "distributed/relation_access_tracking.h"
#include "distributed/replication_origin_session_utils.h"
#include "distributed/shard_utils.h"
#include "distributed/shared_library_init.h"
#include "distributed/tenant_schema_metadata.h"
#include "distributed/shard_utils.h"
#include "distributed/worker_protocol.h"
#include "distributed/worker_transaction.h"
#include "executor/spi.h"
#include "nodes/pg_list.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
/* Table Conversion Types */
@ -200,7 +196,6 @@ static void EnsureTableNotReferencing(Oid relationId, char conversionType);
static void EnsureTableNotReferenced(Oid relationId, char conversionType);
static void EnsureTableNotForeign(Oid relationId);
static void EnsureTableNotPartition(Oid relationId);
static void ErrorIfColocateWithTenantTable(char *colocateWith);
static TableConversionState * CreateTableConversion(TableConversionParameters *params);
static void CreateDistributedTableLike(TableConversionState *con);
static void CreateCitusTableLike(TableConversionState *con);
@ -209,9 +204,12 @@ static void ReplaceTable(Oid sourceId, Oid targetId, List *justBeforeDropCommand
static bool HasAnyGeneratedStoredColumns(Oid relationId);
static List * GetNonGeneratedStoredColumnNameList(Oid relationId);
static void CheckAlterDistributedTableConversionParameters(TableConversionState *con);
static char * CreateWorkerChangeSequenceDependencyCommand(char *qualifiedSequeceName,
char *qualifiedSourceName,
char *qualifiedTargetName);
static char * CreateWorkerChangeSequenceDependencyCommand(char *sequenceSchemaName,
char *sequenceName,
char *sourceSchemaName,
char *sourceName,
char *targetSchemaName,
char *targetName);
static void ErrorIfMatViewSizeExceedsTheLimit(Oid matViewOid);
static char * CreateMaterializedViewDDLCommand(Oid matViewOid);
static char * GetAccessMethodForMatViewIfExists(Oid viewOid);
@ -249,8 +247,7 @@ undistribute_table(PG_FUNCTION_ARGS)
TableConversionParameters params = {
.relationId = relationId,
.cascadeViaForeignKeys = cascadeViaForeignKeys,
.bypassTenantCheck = false
.cascadeViaForeignKeys = cascadeViaForeignKeys
};
UndistributeTable(&params);
@ -363,124 +360,6 @@ worker_change_sequence_dependency(PG_FUNCTION_ARGS)
}
/*
* DropFKeysAndUndistributeTable drops all foreign keys that relation with
* relationId is involved then undistributes it.
* Note that as UndistributeTable changes relationId of relation, this
* function also returns new relationId of relation.
* Also note that callers are responsible for storing & recreating foreign
* keys to be dropped if needed.
*/
Oid
DropFKeysAndUndistributeTable(Oid relationId)
{
DropFKeysRelationInvolvedWithTableType(relationId, INCLUDE_ALL_TABLE_TYPES);
/* store them before calling UndistributeTable as it changes relationId */
char *relationName = get_rel_name(relationId);
Oid schemaId = get_rel_namespace(relationId);
/* suppress notices messages not to be too verbose */
TableConversionParameters params = {
.relationId = relationId,
.cascadeViaForeignKeys = false,
.suppressNoticeMessages = true
};
UndistributeTable(&params);
Oid newRelationId = get_relname_relid(relationName, schemaId);
/*
* We don't expect this to happen but to be on the safe side let's error
* out here.
*/
EnsureRelationExists(newRelationId);
return newRelationId;
}
/*
* UndistributeTables undistributes given relations. It first collects all foreign keys
* to recreate them after the undistribution. Then, drops the foreign keys and
* undistributes the relations. Finally, it recreates foreign keys.
*/
void
UndistributeTables(List *relationIdList)
{
/*
* Collect foreign keys for recreation and then drop fkeys and undistribute
* tables.
*/
List *originalForeignKeyRecreationCommands = NIL;
Oid relationId = InvalidOid;
foreach_declared_oid(relationId, relationIdList)
{
List *fkeyCommandsForRelation =
GetFKeyCreationCommandsRelationInvolvedWithTableType(relationId,
INCLUDE_ALL_TABLE_TYPES);
originalForeignKeyRecreationCommands = list_concat(
originalForeignKeyRecreationCommands, fkeyCommandsForRelation);
DropFKeysAndUndistributeTable(relationId);
}
/* We can skip foreign key validations as we are sure about them at start */
bool skip_validation = true;
ExecuteForeignKeyCreateCommandList(originalForeignKeyRecreationCommands,
skip_validation);
}
/*
* EnsureUndistributeTenantTableSafe ensures that it is safe to undistribute a tenant table.
*/
void
EnsureUndistributeTenantTableSafe(Oid relationId, const char *operationName)
{
Oid schemaId = get_rel_namespace(relationId);
Assert(IsTenantSchema(schemaId));
/* We only allow undistribute while altering schema */
if (strcmp(operationName, TenantOperationNames[TENANT_SET_SCHEMA]) != 0)
{
ErrorIfTenantTable(relationId, operationName);
}
char *tableName = get_rel_name(relationId);
char *schemaName = get_namespace_name(schemaId);
/*
* Partition table cannot be undistributed. Otherwise, its parent table would still
* be a tenant table whereas partition table would be a local table.
*/
if (PartitionTable(relationId))
{
ereport(ERROR, (errmsg("%s is not allowed for partition table %s in distributed "
"schema %s", operationName, tableName, schemaName),
errdetail("partition table should be under the same distributed "
"schema as its parent and be a "
"distributed schema table.")));
}
/*
* When table is referenced by or referencing to a table in the same tenant
* schema, we should disallow undistributing the table since we do not allow
* foreign keys from/to Citus local or Postgres local table to/from distributed
* schema.
*/
List *fkeyCommandsWithSingleShardTables =
GetFKeyCreationCommandsRelationInvolvedWithTableType(
relationId, INCLUDE_SINGLE_SHARD_TABLES);
if (fkeyCommandsWithSingleShardTables != NIL)
{
ereport(ERROR, (errmsg("%s is not allowed for table %s in distributed schema %s",
operationName, tableName, schemaName),
errdetail("distributed schemas cannot have foreign keys from/to "
"local tables or different schema")));
}
}
/*
* UndistributeTable undistributes the given table. It uses ConvertTable function to
* create a new local table and move everything to that table.
@ -501,14 +380,6 @@ UndistributeTable(TableConversionParameters *params)
"because the table is not distributed")));
}
Oid schemaId = get_rel_namespace(params->relationId);
if (!params->bypassTenantCheck && IsTenantSchema(schemaId) &&
IsCitusTableType(params->relationId, SINGLE_SHARD_DISTRIBUTED))
{
EnsureUndistributeTenantTableSafe(params->relationId,
TenantOperationNames[TENANT_UNDISTRIBUTE_TABLE]);
}
if (!params->cascadeViaForeignKeys)
{
EnsureTableNotReferencing(params->relationId, UNDISTRIBUTE_TABLE);
@ -564,9 +435,6 @@ AlterDistributedTable(TableConversionParameters *params)
"is not distributed")));
}
ErrorIfTenantTable(params->relationId, TenantOperationNames[TENANT_ALTER_TABLE]);
ErrorIfColocateWithTenantTable(params->colocateWith);
EnsureTableNotForeign(params->relationId);
EnsureTableNotPartition(params->relationId);
EnsureHashDistributedTable(params->relationId);
@ -609,11 +477,8 @@ AlterTableSetAccessMethod(TableConversionParameters *params)
EnsureTableNotReferencing(params->relationId, ALTER_TABLE_SET_ACCESS_METHOD);
EnsureTableNotReferenced(params->relationId, ALTER_TABLE_SET_ACCESS_METHOD);
EnsureTableNotForeign(params->relationId);
if (!IsCitusTableType(params->relationId, SINGLE_SHARD_DISTRIBUTED) &&
IsCitusTableType(params->relationId, DISTRIBUTED_TABLE))
if (IsCitusTableType(params->relationId, DISTRIBUTED_TABLE))
{
/* we do not support non-hash distributed tables, except single shard tables */
EnsureHashDistributedTable(params->relationId);
}
@ -788,21 +653,19 @@ ConvertTableInternal(TableConversionState *con)
justBeforeDropCommands = lappend(justBeforeDropCommands, detachFromParentCommand);
}
char *qualifiedRelationName = quote_qualified_identifier(con->schemaName,
con->relationName);
if (PartitionedTable(con->relationId))
{
if (!con->suppressNoticeMessages)
{
ereport(NOTICE, (errmsg("converting the partitions of %s",
qualifiedRelationName)));
quote_qualified_identifier(con->schemaName,
con->relationName))));
}
List *partitionList = PartitionList(con->relationId);
Oid partitionRelationId = InvalidOid;
foreach_declared_oid(partitionRelationId, partitionList)
foreach_oid(partitionRelationId, partitionList)
{
char *tableQualifiedName = generate_qualified_relation_name(
partitionRelationId);
@ -869,11 +732,13 @@ ConvertTableInternal(TableConversionState *con)
if (!con->suppressNoticeMessages)
{
ereport(NOTICE, (errmsg("creating a new table for %s", qualifiedRelationName)));
ereport(NOTICE, (errmsg("creating a new table for %s",
quote_qualified_identifier(con->schemaName,
con->relationName))));
}
TableDDLCommand *tableCreationCommand = NULL;
foreach_declared_ptr(tableCreationCommand, preLoadCommands)
foreach_ptr(tableCreationCommand, preLoadCommands)
{
Assert(CitusIsA(tableCreationCommand, TableDDLCommand));
@ -947,7 +812,7 @@ ConvertTableInternal(TableConversionState *con)
con->suppressNoticeMessages);
TableDDLCommand *tableConstructionCommand = NULL;
foreach_declared_ptr(tableConstructionCommand, postLoadCommands)
foreach_ptr(tableConstructionCommand, postLoadCommands)
{
Assert(CitusIsA(tableConstructionCommand, TableDDLCommand));
char *tableConstructionSQL = GetTableDDLCommand(tableConstructionCommand);
@ -965,7 +830,7 @@ ConvertTableInternal(TableConversionState *con)
MemoryContext oldContext = MemoryContextSwitchTo(citusPerPartitionContext);
char *attachPartitionCommand = NULL;
foreach_declared_ptr(attachPartitionCommand, attachPartitionCommands)
foreach_ptr(attachPartitionCommand, attachPartitionCommands)
{
MemoryContextReset(citusPerPartitionContext);
@ -990,12 +855,14 @@ ConvertTableInternal(TableConversionState *con)
/* For now we only support cascade to colocation for alter_distributed_table UDF */
Assert(con->conversionType == ALTER_DISTRIBUTED_TABLE);
foreach_declared_oid(colocatedTableId, con->colocatedTableList)
foreach_oid(colocatedTableId, con->colocatedTableList)
{
if (colocatedTableId == con->relationId)
{
continue;
}
char *qualifiedRelationName = quote_qualified_identifier(con->schemaName,
con->relationName);
TableConversionParameters cascadeParam = {
.relationId = colocatedTableId,
@ -1018,7 +885,7 @@ ConvertTableInternal(TableConversionState *con)
if (con->cascadeToColocated != CASCADE_TO_COLOCATED_NO_ALREADY_CASCADED)
{
char *foreignKeyCommand = NULL;
foreach_declared_ptr(foreignKeyCommand, foreignKeyCommands)
foreach_ptr(foreignKeyCommand, foreignKeyCommands)
{
ExecuteQueryViaSPI(foreignKeyCommand, SPI_OK_UTILITY);
}
@ -1054,7 +921,7 @@ CopyTableConversionReturnIntoCurrentContext(TableConversionReturn *tableConversi
tableConversionReturnCopy = palloc0(sizeof(TableConversionReturn));
List *copyForeignKeyCommands = NIL;
char *foreignKeyCommand = NULL;
foreach_declared_ptr(foreignKeyCommand, tableConversionReturn->foreignKeyCommands)
foreach_ptr(foreignKeyCommand, tableConversionReturn->foreignKeyCommands)
{
char *copyForeignKeyCommand = MemoryContextStrdup(CurrentMemoryContext,
foreignKeyCommand);
@ -1129,7 +996,7 @@ DropIndexesNotSupportedByColumnar(Oid relationId, bool suppressNoticeMessages)
RelationClose(columnarRelation);
Oid indexId = InvalidOid;
foreach_declared_oid(indexId, indexIdList)
foreach_oid(indexId, indexIdList)
{
char *indexAmName = GetIndexAccessMethodName(indexId);
if (extern_ColumnarSupportsIndexAM(indexAmName))
@ -1310,25 +1177,6 @@ EnsureTableNotPartition(Oid relationId)
}
/*
* ErrorIfColocateWithTenantTable errors out if given colocateWith text refers to
* a tenant table.
*/
void
ErrorIfColocateWithTenantTable(char *colocateWith)
{
if (colocateWith != NULL &&
!IsColocateWithDefault(colocateWith) &&
!IsColocateWithNone(colocateWith))
{
text *colocateWithTableNameText = cstring_to_text(colocateWith);
Oid colocateWithTableId = ResolveRelationId(colocateWithTableNameText, false);
ErrorIfTenantTable(colocateWithTableId,
TenantOperationNames[TENANT_COLOCATE_WITH]);
}
}
TableConversionState *
CreateTableConversion(TableConversionParameters *params)
{
@ -1389,7 +1237,7 @@ CreateTableConversion(TableConversionParameters *params)
* since they will be handled separately.
*/
Oid colocatedTableId = InvalidOid;
foreach_declared_oid(colocatedTableId, colocatedTableList)
foreach_oid(colocatedTableId, colocatedTableList)
{
if (PartitionTable(colocatedTableId))
{
@ -1517,19 +1365,7 @@ CreateCitusTableLike(TableConversionState *con)
{
if (IsCitusTableType(con->relationId, DISTRIBUTED_TABLE))
{
if (IsCitusTableType(con->relationId, SINGLE_SHARD_DISTRIBUTED))
{
ColocationParam colocationParam = {
.colocationParamType = COLOCATE_WITH_TABLE_LIKE_OPT,
.colocateWithTableName = quote_qualified_identifier(con->schemaName,
con->relationName)
};
CreateSingleShardTable(con->newRelationId, colocationParam);
}
else
{
CreateDistributedTableLike(con);
}
CreateDistributedTableLike(con);
}
else if (IsCitusTableType(con->relationId, REFERENCE_TABLE))
{
@ -1605,7 +1441,7 @@ DoesCascadeDropUnsupportedObject(Oid classId, Oid objectId, HTAB *nodeMap)
targetObjectId);
HeapTuple depTup = NULL;
foreach_declared_ptr(depTup, dependencyTupleList)
foreach_ptr(depTup, dependencyTupleList)
{
Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
@ -1645,7 +1481,7 @@ GetViewCreationCommandsOfTable(Oid relationId)
List *commands = NIL;
Oid viewOid = InvalidOid;
foreach_declared_oid(viewOid, views)
foreach_oid(viewOid, views)
{
StringInfo query = makeStringInfo();
@ -1683,7 +1519,7 @@ WrapTableDDLCommands(List *commandStrings)
List *tableDDLCommands = NIL;
char *command = NULL;
foreach_declared_ptr(command, commandStrings)
foreach_ptr(command, commandStrings)
{
tableDDLCommands = lappend(tableDDLCommands, makeTableDDLCommandString(command));
}
@ -1745,7 +1581,9 @@ CreateMaterializedViewDDLCommand(Oid matViewOid)
{
StringInfo query = makeStringInfo();
char *qualifiedViewName = generate_qualified_relation_name(matViewOid);
char *viewName = get_rel_name(matViewOid);
char *schemaName = get_namespace_name(get_rel_namespace(matViewOid));
char *qualifiedViewName = quote_qualified_identifier(schemaName, viewName);
/* here we need to get the access method of the view to recreate it */
char *accessMethodName = GetAccessMethodForMatViewIfExists(matViewOid);
@ -1761,7 +1599,10 @@ CreateMaterializedViewDDLCommand(Oid matViewOid)
* Set search_path to NIL so that all objects outside of pg_catalog will be
* schema-prefixed.
*/
int saveNestLevel = PushEmptySearchPath();
OverrideSearchPath *overridePath = GetOverrideSearchPath(CurrentMemoryContext);
overridePath->schemas = NIL;
overridePath->addCatalog = true;
PushOverrideSearchPath(overridePath);
/*
* Push the transaction snapshot to be able to get vief definition with pg_get_viewdef
@ -1773,7 +1614,7 @@ CreateMaterializedViewDDLCommand(Oid matViewOid)
char *viewDefinition = TextDatumGetCString(viewDefinitionDatum);
PopActiveSnapshot();
PopEmptySearchPath(saveNestLevel);
PopOverrideSearchPath();
appendStringInfo(query, "AS %s", viewDefinition);
@ -1794,8 +1635,9 @@ ReplaceTable(Oid sourceId, Oid targetId, List *justBeforeDropCommands,
bool suppressNoticeMessages)
{
char *sourceName = get_rel_name(sourceId);
char *qualifiedSourceName = generate_qualified_relation_name(sourceId);
char *qualifiedTargetName = generate_qualified_relation_name(targetId);
char *targetName = get_rel_name(targetId);
Oid schemaId = get_rel_namespace(sourceId);
char *schemaName = get_namespace_name(schemaId);
StringInfo query = makeStringInfo();
@ -1803,7 +1645,8 @@ ReplaceTable(Oid sourceId, Oid targetId, List *justBeforeDropCommands,
{
if (!suppressNoticeMessages)
{
ereport(NOTICE, (errmsg("moving the data of %s", qualifiedSourceName)));
ereport(NOTICE, (errmsg("moving the data of %s",
quote_qualified_identifier(schemaName, sourceName))));
}
if (!HasAnyGeneratedStoredColumns(sourceId))
@ -1813,7 +1656,8 @@ ReplaceTable(Oid sourceId, Oid targetId, List *justBeforeDropCommands,
* "INSERT INTO .. SELECT *"".
*/
appendStringInfo(query, "INSERT INTO %s SELECT * FROM %s",
qualifiedTargetName, qualifiedSourceName);
quote_qualified_identifier(schemaName, targetName),
quote_qualified_identifier(schemaName, sourceName));
}
else
{
@ -1828,8 +1672,9 @@ ReplaceTable(Oid sourceId, Oid targetId, List *justBeforeDropCommands,
char *insertColumnString = StringJoin(nonStoredColumnNameList, ',');
appendStringInfo(query,
"INSERT INTO %s (%s) OVERRIDING SYSTEM VALUE SELECT %s FROM %s",
qualifiedTargetName, insertColumnString,
insertColumnString, qualifiedSourceName);
quote_qualified_identifier(schemaName, targetName),
insertColumnString, insertColumnString,
quote_qualified_identifier(schemaName, sourceName));
}
ExecuteQueryViaSPI(query->data, SPI_OK_INSERT);
@ -1840,7 +1685,7 @@ ReplaceTable(Oid sourceId, Oid targetId, List *justBeforeDropCommands,
*/
List *ownedSequences = getOwnedSequences_internal(sourceId, 0, DEPENDENCY_AUTO);
Oid sequenceOid = InvalidOid;
foreach_declared_oid(sequenceOid, ownedSequences)
foreach_oid(sequenceOid, ownedSequences)
{
changeDependencyFor(RelationRelationId, sequenceOid,
RelationRelationId, sourceId, targetId);
@ -1853,11 +1698,14 @@ ReplaceTable(Oid sourceId, Oid targetId, List *justBeforeDropCommands,
*/
if (ShouldSyncTableMetadata(targetId))
{
char *qualifiedSequenceName = generate_qualified_relation_name(sequenceOid);
Oid sequenceSchemaOid = get_rel_namespace(sequenceOid);
char *sequenceSchemaName = get_namespace_name(sequenceSchemaOid);
char *sequenceName = get_rel_name(sequenceOid);
char *workerChangeSequenceDependencyCommand =
CreateWorkerChangeSequenceDependencyCommand(qualifiedSequenceName,
qualifiedSourceName,
qualifiedTargetName);
CreateWorkerChangeSequenceDependencyCommand(sequenceSchemaName,
sequenceName,
schemaName, sourceName,
schemaName, targetName);
SendCommandToWorkersWithMetadata(workerChangeSequenceDependencyCommand);
}
else if (ShouldSyncTableMetadata(sourceId))
@ -1873,30 +1721,32 @@ ReplaceTable(Oid sourceId, Oid targetId, List *justBeforeDropCommands,
}
char *justBeforeDropCommand = NULL;
foreach_declared_ptr(justBeforeDropCommand, justBeforeDropCommands)
foreach_ptr(justBeforeDropCommand, justBeforeDropCommands)
{
ExecuteQueryViaSPI(justBeforeDropCommand, SPI_OK_UTILITY);
}
if (!suppressNoticeMessages)
{
ereport(NOTICE, (errmsg("dropping the old %s", qualifiedSourceName)));
ereport(NOTICE, (errmsg("dropping the old %s",
quote_qualified_identifier(schemaName, sourceName))));
}
resetStringInfo(query);
appendStringInfo(query, "DROP %sTABLE %s CASCADE",
IsForeignTable(sourceId) ? "FOREIGN " : "",
qualifiedSourceName);
quote_qualified_identifier(schemaName, sourceName));
ExecuteQueryViaSPI(query->data, SPI_OK_UTILITY);
if (!suppressNoticeMessages)
{
ereport(NOTICE, (errmsg("renaming the new table to %s", qualifiedSourceName)));
ereport(NOTICE, (errmsg("renaming the new table to %s",
quote_qualified_identifier(schemaName, sourceName))));
}
resetStringInfo(query);
appendStringInfo(query, "ALTER TABLE %s RENAME TO %s",
qualifiedTargetName,
quote_qualified_identifier(schemaName, targetName),
quote_identifier(sourceName));
ExecuteQueryViaSPI(query->data, SPI_OK_UTILITY);
}
@ -1987,7 +1837,7 @@ CheckAlterDistributedTableConversionParameters(TableConversionState *con)
Oid colocatedTableOid = InvalidOid;
text *colocateWithText = cstring_to_text(con->colocateWith);
Oid colocateWithTableOid = ResolveRelationId(colocateWithText, false);
foreach_declared_oid(colocatedTableOid, con->colocatedTableList)
foreach_oid(colocatedTableOid, con->colocatedTableList)
{
if (colocateWithTableOid == colocatedTableOid)
{
@ -2006,12 +1856,6 @@ CheckAlterDistributedTableConversionParameters(TableConversionState *con)
"it is not a distributed table",
con->colocateWith)));
}
else if (IsCitusTableType(colocateWithTableOid, SINGLE_SHARD_DISTRIBUTED))
{
ereport(ERROR, (errmsg("cannot colocate with %s because "
"it is a single shard distributed table",
con->colocateWith)));
}
}
/* shard_count:=0 is not allowed */
@ -2156,13 +2000,18 @@ CheckAlterDistributedTableConversionParameters(TableConversionState *con)
* worker_change_sequence_dependency query with the parameters.
*/
static char *
CreateWorkerChangeSequenceDependencyCommand(char *qualifiedSequeceName,
char *qualifiedSourceName,
char *qualifiedTargetName)
CreateWorkerChangeSequenceDependencyCommand(char *sequenceSchemaName, char *sequenceName,
char *sourceSchemaName, char *sourceName,
char *targetSchemaName, char *targetName)
{
char *qualifiedSchemaName = quote_qualified_identifier(sequenceSchemaName,
sequenceName);
char *qualifiedSourceName = quote_qualified_identifier(sourceSchemaName, sourceName);
char *qualifiedTargetName = quote_qualified_identifier(targetSchemaName, targetName);
StringInfo query = makeStringInfo();
appendStringInfo(query, "SELECT worker_change_sequence_dependency(%s, %s, %s)",
quote_literal_cstr(qualifiedSequeceName),
quote_literal_cstr(qualifiedSchemaName),
quote_literal_cstr(qualifiedSourceName),
quote_literal_cstr(qualifiedTargetName));
@ -2214,7 +2063,7 @@ WillRecreateForeignKeyToReferenceTable(Oid relationId,
{
List *colocatedTableList = ColocatedTableList(relationId);
Oid colocatedTableOid = InvalidOid;
foreach_declared_oid(colocatedTableOid, colocatedTableList)
foreach_oid(colocatedTableOid, colocatedTableList)
{
if (HasForeignKeyToReferenceTable(colocatedTableOid))
{
@ -2242,7 +2091,7 @@ WarningsForDroppingForeignKeysWithDistributedTables(Oid relationId)
List *foreignKeys = list_concat(referencingForeingKeys, referencedForeignKeys);
Oid foreignKeyOid = InvalidOid;
foreach_declared_oid(foreignKeyOid, foreignKeys)
foreach_oid(foreignKeyOid, foreignKeys)
{
ereport(WARNING, (errmsg("foreign key %s will be dropped",
get_constraint_name(foreignKeyOid))));

View File

@ -9,14 +9,12 @@
*/
#include "postgres.h"
#include "c.h"
#include "nodes/parsenodes.h"
#include "distributed/commands.h"
#include "distributed/listutils.h"
#include "distributed/transaction_management.h"
#include "nodes/parsenodes.h"
/*
@ -33,7 +31,7 @@ SaveBeginCommandProperties(TransactionStmt *transactionStmt)
*
* While BEGIN can be quite frequent it will rarely have options set.
*/
foreach_declared_ptr(item, transactionStmt->options)
foreach_ptr(item, transactionStmt->options)
{
A_Const *constant = (A_Const *) item->arg;

View File

@ -11,23 +11,12 @@
*/
#include "postgres.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "distributed/pg_version_constants.h"
#include "catalog/pg_proc.h"
#include "commands/defrem.h"
#include "nodes/nodeFuncs.h"
#include "nodes/parsenodes.h"
#include "nodes/primnodes.h"
#include "optimizer/clauses.h"
#include "tcop/dest.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
#include "pg_version_constants.h"
#include "distributed/adaptive_executor.h"
#include "distributed/backend_data.h"
#include "distributed/citus_ruleutils.h"
#include "distributed/colocation_utils.h"
@ -37,17 +26,27 @@
#include "distributed/connection_management.h"
#include "distributed/deparse_shard_query.h"
#include "distributed/function_call_delegation.h"
#include "distributed/metadata_cache.h"
#include "distributed/metadata_utility.h"
#include "distributed/metadata_cache.h"
#include "distributed/multi_executor.h"
#include "distributed/multi_physical_planner.h"
#include "distributed/adaptive_executor.h"
#include "distributed/reference_table_utils.h"
#include "distributed/remote_commands.h"
#include "distributed/reference_table_utils.h"
#include "distributed/shard_pruning.h"
#include "distributed/tuple_destination.h"
#include "distributed/version_compat.h"
#include "distributed/worker_log_messages.h"
#include "distributed/worker_manager.h"
#include "distributed/worker_log_messages.h"
#include "optimizer/clauses.h"
#include "nodes/nodeFuncs.h"
#include "nodes/parsenodes.h"
#include "nodes/primnodes.h"
#include "miscadmin.h"
#include "tcop/dest.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
/* global variable tracking whether we are in a delegated procedure call */

View File

@ -12,19 +12,12 @@
#include "postgres.h"
#include "miscadmin.h"
#include "distributed/pg_version_constants.h"
#include "access/xact.h"
#include "catalog/pg_constraint.h"
#include "executor/spi.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
#include "pg_version_constants.h"
#include "distributed/commands.h"
#include "distributed/commands/utility_hook.h"
#include "distributed/commands.h"
#include "distributed/foreign_key_relationship.h"
#include "distributed/listutils.h"
#include "distributed/local_executor.h"
@ -33,6 +26,11 @@
#include "distributed/reference_table_utils.h"
#include "distributed/relation_access_tracking.h"
#include "distributed/worker_protocol.h"
#include "executor/spi.h"
#include "miscadmin.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
static void EnsureSequentialModeForCitusTableCascadeFunction(List *relationIdList);
@ -168,7 +166,7 @@ GetPartitionRelationIds(List *relationIdList)
List *partitionRelationIdList = NIL;
Oid relationId = InvalidOid;
foreach_declared_oid(relationId, relationIdList)
foreach_oid(relationId, relationIdList)
{
if (PartitionTable(relationId))
{
@ -189,7 +187,7 @@ LockRelationsWithLockMode(List *relationIdList, LOCKMODE lockMode)
{
Oid relationId;
relationIdList = SortList(relationIdList, CompareOids);
foreach_declared_oid(relationId, relationIdList)
foreach_oid(relationId, relationIdList)
{
LockRelationOid(relationId, lockMode);
}
@ -207,7 +205,7 @@ static void
ErrorIfConvertingMultiLevelPartitionedTable(List *relationIdList)
{
Oid relationId;
foreach_declared_oid(relationId, relationIdList)
foreach_oid(relationId, relationIdList)
{
if (PartitionedTable(relationId) && PartitionTable(relationId))
{
@ -236,7 +234,7 @@ void
ErrorIfAnyPartitionRelationInvolvedInNonInheritedFKey(List *relationIdList)
{
Oid relationId = InvalidOid;
foreach_declared_oid(relationId, relationIdList)
foreach_oid(relationId, relationIdList)
{
if (!PartitionTable(relationId))
{
@ -300,7 +298,7 @@ bool
RelationIdListHasReferenceTable(List *relationIdList)
{
Oid relationId = InvalidOid;
foreach_declared_oid(relationId, relationIdList)
foreach_oid(relationId, relationIdList)
{
if (IsCitusTableType(relationId, REFERENCE_TABLE))
{
@ -322,7 +320,7 @@ GetFKeyCreationCommandsForRelationIdList(List *relationIdList)
List *fKeyCreationCommands = NIL;
Oid relationId = InvalidOid;
foreach_declared_oid(relationId, relationIdList)
foreach_oid(relationId, relationIdList)
{
List *relationFKeyCreationCommands =
GetReferencingForeignConstaintCommands(relationId);
@ -342,7 +340,7 @@ static void
DropRelationIdListForeignKeys(List *relationIdList, int fKeyFlags)
{
Oid relationId = InvalidOid;
foreach_declared_oid(relationId, relationIdList)
foreach_oid(relationId, relationIdList)
{
DropRelationForeignKeys(relationId, fKeyFlags);
}
@ -399,7 +397,7 @@ GetRelationDropFkeyCommands(Oid relationId, int fKeyFlags)
List *relationFKeyIdList = GetForeignKeyOids(relationId, fKeyFlags);
Oid foreignKeyId;
foreach_declared_oid(foreignKeyId, relationFKeyIdList)
foreach_oid(foreignKeyId, relationFKeyIdList)
{
char *dropFkeyCascadeCommand = GetDropFkeyCascadeCommand(foreignKeyId);
dropFkeyCascadeCommandList = lappend(dropFkeyCascadeCommandList,
@ -450,7 +448,7 @@ ExecuteCascadeOperationForRelationIdList(List *relationIdList,
cascadeOperationType)
{
Oid relationId = InvalidOid;
foreach_declared_oid(relationId, relationIdList)
foreach_oid(relationId, relationIdList)
{
/*
* The reason behind skipping certain table types in below loop is
@ -470,8 +468,7 @@ ExecuteCascadeOperationForRelationIdList(List *relationIdList,
{
TableConversionParameters params = {
.relationId = relationId,
.cascadeViaForeignKeys = cascadeViaForeignKeys,
.bypassTenantCheck = false
.cascadeViaForeignKeys = cascadeViaForeignKeys
};
UndistributeTable(&params);
}
@ -531,7 +528,7 @@ ExecuteAndLogUtilityCommandListInTableTypeConversionViaSPI(List *utilityCommandL
PG_TRY();
{
char *utilityCommand = NULL;
foreach_declared_ptr(utilityCommand, utilityCommandList)
foreach_ptr(utilityCommand, utilityCommandList)
{
/*
* CREATE MATERIALIZED VIEW commands need to be parsed/transformed,
@ -569,7 +566,7 @@ void
ExecuteAndLogUtilityCommandList(List *utilityCommandList)
{
char *utilityCommand = NULL;
foreach_declared_ptr(utilityCommand, utilityCommandList)
foreach_ptr(utilityCommand, utilityCommandList)
{
ExecuteAndLogUtilityCommand(utilityCommand);
}
@ -597,7 +594,7 @@ void
ExecuteForeignKeyCreateCommandList(List *ddlCommandList, bool skip_validation)
{
char *ddlCommand = NULL;
foreach_declared_ptr(ddlCommand, ddlCommandList)
foreach_ptr(ddlCommand, ddlCommandList)
{
ExecuteForeignKeyCreateCommand(ddlCommand, skip_validation);
}

View File

@ -19,37 +19,34 @@
#include "postgres.h"
#include "miscadmin.h"
#include "access/genam.h"
#include "access/htup_details.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_trigger.h"
#include "foreign/foreign.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/ruleutils.h"
#include "utils/syscache.h"
#include "distributed/coordinator_protocol.h"
#include "distributed/citus_ruleutils.h"
#include "distributed/colocation_utils.h"
#include "distributed/commands.h"
#include "distributed/commands/sequence.h"
#include "distributed/commands/utility_hook.h"
#include "distributed/coordinator_protocol.h"
#include "distributed/metadata/distobject.h"
#include "distributed/metadata/dependency.h"
#include "distributed/foreign_key_relationship.h"
#include "distributed/listutils.h"
#include "distributed/local_executor.h"
#include "distributed/metadata/dependency.h"
#include "distributed/metadata/distobject.h"
#include "distributed/metadata_sync.h"
#include "distributed/multi_partitioning_utils.h"
#include "distributed/namespace_utils.h"
#include "distributed/reference_table_utils.h"
#include "distributed/worker_protocol.h"
#include "distributed/worker_shard_visibility.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/ruleutils.h"
#include "utils/syscache.h"
#include "foreign/foreign.h"
/*
@ -57,7 +54,7 @@
* This is used after every CREATE TABLE statement in utility_hook.c
* If this variable is set to true, we add all created tables to metadata.
*/
bool AddAllLocalTablesToMetadata = false;
bool AddAllLocalTablesToMetadata = true;
static void citus_add_local_table_to_metadata_internal(Oid relationId,
bool cascadeViaForeignKeys);
@ -588,7 +585,7 @@ ErrorIfOptionListHasNoTableName(List *optionList)
{
char *table_nameString = "table_name";
DefElem *option = NULL;
foreach_declared_ptr(option, optionList)
foreach_ptr(option, optionList)
{
char *optionName = option->defname;
if (strcmp(optionName, table_nameString) == 0)
@ -613,7 +610,7 @@ ForeignTableDropsTableNameOption(List *optionList)
{
char *table_nameString = "table_name";
DefElem *option = NULL;
foreach_declared_ptr(option, optionList)
foreach_ptr(option, optionList)
{
char *optionName = option->defname;
DefElemAction optionAction = option->defaction;
@ -732,7 +729,7 @@ UpdateAutoConvertedForConnectedRelations(List *relationIds, bool autoConverted)
List *relationIdList = NIL;
Oid relid = InvalidOid;
foreach_declared_oid(relid, relationIds)
foreach_oid(relid, relationIds)
{
List *connectedRelations = GetForeignKeyConnectedRelationIdList(relid);
relationIdList = list_concat_unique_oid(relationIdList, connectedRelations);
@ -740,7 +737,7 @@ UpdateAutoConvertedForConnectedRelations(List *relationIds, bool autoConverted)
relationIdList = SortList(relationIdList, CompareOids);
foreach_declared_oid(relid, relationIdList)
foreach_oid(relid, relationIdList)
{
UpdatePgDistPartitionAutoConverted(relid, autoConverted);
}
@ -776,7 +773,7 @@ GetShellTableDDLEventsForCitusLocalTable(Oid relationId)
List *shellTableDDLEvents = NIL;
TableDDLCommand *tableDDLCommand = NULL;
foreach_declared_ptr(tableDDLCommand, tableDDLCommands)
foreach_ptr(tableDDLCommand, tableDDLCommands)
{
Assert(CitusIsA(tableDDLCommand, TableDDLCommand));
shellTableDDLEvents = lappend(shellTableDDLEvents,
@ -863,7 +860,7 @@ RenameShardRelationConstraints(Oid shardRelationId, uint64 shardId)
List *constraintNameList = GetConstraintNameList(shardRelationId);
char *constraintName = NULL;
foreach_declared_ptr(constraintName, constraintNameList)
foreach_ptr(constraintName, constraintNameList)
{
const char *commandString =
GetRenameShardConstraintCommand(shardRelationId, constraintName, shardId);
@ -894,7 +891,7 @@ GetConstraintNameList(Oid relationId)
Relation pgConstraint = table_open(ConstraintRelationId, AccessShareLock);
ScanKeyInit(&scanKey[0], Anum_pg_constraint_conrelid,
BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relationId));
BTEqualStrategyNumber, F_OIDEQ, relationId);
bool useIndex = true;
SysScanDesc scanDescriptor = systable_beginscan(pgConstraint,
@ -958,7 +955,7 @@ RenameShardRelationIndexes(Oid shardRelationId, uint64 shardId)
List *indexOidList = GetExplicitIndexOidList(shardRelationId);
Oid indexOid = InvalidOid;
foreach_declared_oid(indexOid, indexOidList)
foreach_oid(indexOid, indexOidList)
{
const char *commandString = GetRenameShardIndexCommand(indexOid, shardId);
ExecuteAndLogUtilityCommand(commandString);
@ -1008,7 +1005,7 @@ RenameShardRelationStatistics(Oid shardRelationId, uint64 shardId)
List *statsCommandList = GetRenameStatsCommandList(statsOidList, shardId);
char *command = NULL;
foreach_declared_ptr(command, statsCommandList)
foreach_ptr(command, statsCommandList)
{
ExecuteAndLogUtilityCommand(command);
}
@ -1044,7 +1041,7 @@ RenameShardRelationNonTruncateTriggers(Oid shardRelationId, uint64 shardId)
List *triggerIdList = GetExplicitTriggerIdList(shardRelationId);
Oid triggerId = InvalidOid;
foreach_declared_oid(triggerId, triggerIdList)
foreach_oid(triggerId, triggerIdList)
{
bool missingOk = false;
HeapTuple triggerTuple = GetTriggerTupleById(triggerId, missingOk);
@ -1097,7 +1094,7 @@ DropRelationTruncateTriggers(Oid relationId)
List *triggerIdList = GetExplicitTriggerIdList(relationId);
Oid triggerId = InvalidOid;
foreach_declared_oid(triggerId, triggerIdList)
foreach_oid(triggerId, triggerIdList)
{
bool missingOk = false;
HeapTuple triggerTuple = GetTriggerTupleById(triggerId, missingOk);
@ -1160,7 +1157,9 @@ DropIdentitiesOnTable(Oid relationId)
if (attributeForm->attidentity)
{
char *qualifiedTableName = generate_qualified_relation_name(relationId);
char *tableName = get_rel_name(relationId);
char *schemaName = get_namespace_name(get_rel_namespace(relationId));
char *qualifiedTableName = quote_qualified_identifier(schemaName, tableName);
StringInfo dropCommand = makeStringInfo();
@ -1175,7 +1174,7 @@ DropIdentitiesOnTable(Oid relationId)
relation_close(relation, NoLock);
char *dropCommand = NULL;
foreach_declared_ptr(dropCommand, dropCommandList)
foreach_ptr(dropCommand, dropCommandList)
{
/*
* We need to disable/enable ddl propagation for this command, to prevent
@ -1218,9 +1217,11 @@ DropViewsOnTable(Oid relationId)
List *reverseOrderedViews = ReversedOidList(views);
Oid viewId = InvalidOid;
foreach_declared_oid(viewId, reverseOrderedViews)
foreach_oid(viewId, reverseOrderedViews)
{
char *qualifiedViewName = generate_qualified_relation_name(viewId);
char *viewName = get_rel_name(viewId);
char *schemaName = get_namespace_name(get_rel_namespace(viewId));
char *qualifiedViewName = quote_qualified_identifier(schemaName, viewName);
StringInfo dropCommand = makeStringInfo();
appendStringInfo(dropCommand, "DROP %sVIEW IF EXISTS %s",
@ -1241,7 +1242,7 @@ ReversedOidList(List *oidList)
{
List *reversed = NIL;
Oid oid = InvalidOid;
foreach_declared_oid(oid, oidList)
foreach_oid(oid, oidList)
{
reversed = lcons_oid(oid, reversed);
}
@ -1293,7 +1294,7 @@ GetRenameStatsCommandList(List *statsOidList, uint64 shardId)
{
List *statsCommandList = NIL;
Oid statsOid;
foreach_declared_oid(statsOid, statsOidList)
foreach_oid(statsOid, statsOidList)
{
HeapTuple tup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statsOid));
@ -1476,20 +1477,11 @@ InsertMetadataForCitusLocalTable(Oid citusLocalTableId, uint64 shardId,
static void
FinalizeCitusLocalTableCreation(Oid relationId)
{
#if PG_VERSION_NUM >= PG_VERSION_16
/*
* PG16+ supports truncate triggers on foreign tables
*/
if (RegularTable(relationId) || IsForeignTable(relationId))
#else
/*
* If it is a foreign table, then skip creating citus truncate trigger
* as foreign tables do not support truncate triggers.
*/
if (RegularTable(relationId))
#endif
{
CreateTruncateTrigger(relationId);
}
@ -1508,38 +1500,3 @@ FinalizeCitusLocalTableCreation(Oid relationId)
InvalidateForeignKeyGraph();
}
}
/*
* ShouldAddNewTableToMetadata takes a relationId and returns true if we need to add a
* newly created table to metadata, false otherwise.
* For partitions and temporary tables, ShouldAddNewTableToMetadata returns false.
* For other tables created, returns true, if we are on a coordinator that is added
* as worker, and ofcourse, if the GUC use_citus_managed_tables is set to on.
*/
bool
ShouldAddNewTableToMetadata(Oid relationId)
{
if (get_rel_persistence(relationId) == RELPERSISTENCE_TEMP ||
PartitionTableNoLock(relationId))
{
/*
* Shouldn't add table to metadata if it's a temp table, or a partition.
* Creating partitions of a table that is added to metadata is already handled.
*/
return false;
}
if (AddAllLocalTablesToMetadata && !IsBinaryUpgrade &&
IsCoordinator() && CoordinatorAddedAsWorkerNode())
{
/*
* We have verified that the GUC is set to true, and we are not upgrading,
* and we are on the coordinator that is added as worker node.
* So return true here, to add this newly created table to metadata.
*/
return true;
}
return false;
}

View File

@ -11,16 +11,14 @@
#include "postgres.h"
#include "signal.h"
#include "lib/stringinfo.h"
#include "pg_version_constants.h"
#include "distributed/pg_version_constants.h"
#include "distributed/backend_data.h"
#include "distributed/metadata_cache.h"
#include "distributed/remote_commands.h"
#include "distributed/worker_manager.h"
#include "lib/stringinfo.h"
#include "signal.h"
static bool CitusSignalBackend(uint64 globalPID, uint64 timeout, int sig);
@ -83,6 +81,13 @@ CitusSignalBackend(uint64 globalPID, uint64 timeout, int sig)
{
Assert((sig == SIGINT) || (sig == SIGTERM));
#if PG_VERSION_NUM < PG_VERSION_14
if (timeout != 0)
{
elog(ERROR, "timeout parameter is only supported on Postgres 14 or later");
}
#endif
bool missingOk = false;
int nodeId = ExtractNodeIdFromGlobalPID(globalPID, missingOk);
int processId = ExtractProcessIdFromGlobalPID(globalPID);
@ -97,9 +102,14 @@ CitusSignalBackend(uint64 globalPID, uint64 timeout, int sig)
}
else
{
#if PG_VERSION_NUM >= PG_VERSION_14
appendStringInfo(cancelQuery,
"SELECT pg_terminate_backend(%d::integer, %lu::bigint)",
processId, timeout);
#else
appendStringInfo(cancelQuery, "SELECT pg_terminate_backend(%d::integer)",
processId);
#endif
}
int connectionFlags = 0;

View File

@ -10,11 +10,11 @@
#include "postgres.h"
#include "catalog/namespace.h"
#include "distributed/pg_version_constants.h"
#include "commands/defrem.h"
#include "pg_version_constants.h"
#include "catalog/namespace.h"
#include "distributed/commands.h"
#include "distributed/commands/utility_hook.h"
#include "distributed/listutils.h"
@ -114,8 +114,15 @@ PreprocessClusterStmt(Node *node, const char *clusterCommand,
static bool
IsClusterStmtVerbose_compat(ClusterStmt *clusterStmt)
{
#if PG_VERSION_NUM < PG_VERSION_14
if (clusterStmt->options & CLUOPT_VERBOSE)
{
return true;
}
return false;
#else
DefElem *opt = NULL;
foreach_declared_ptr(opt, clusterStmt->params)
foreach_ptr(opt, clusterStmt->params)
{
if (strcmp(opt->defname, "verbose") == 0)
{
@ -123,4 +130,5 @@ IsClusterStmtVerbose_compat(ClusterStmt *clusterStmt)
}
}
return false;
#endif
}

View File

@ -10,32 +10,30 @@
*/
#include "postgres.h"
#include "miscadmin.h"
#include "pg_version_compat.h"
#include "access/htup_details.h"
#include "access/xact.h"
#include "catalog/pg_collation.h"
#include "distributed/citus_safe_lib.h"
#include "distributed/commands/utility_hook.h"
#include "distributed/commands.h"
#include "distributed/deparser.h"
#include "distributed/listutils.h"
#include "distributed/metadata_utility.h"
#include "distributed/metadata/dependency.h"
#include "distributed/metadata/distobject.h"
#include "distributed/metadata_sync.h"
#include "distributed/multi_executor.h"
#include "distributed/relation_access_tracking.h"
#include "distributed/worker_create_or_replace.h"
#include "distributed/pg_version_constants.h"
#include "distributed/worker_manager.h"
#include "parser/parse_type.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
#include "pg_version_compat.h"
#include "pg_version_constants.h"
#include "distributed/citus_safe_lib.h"
#include "distributed/commands.h"
#include "distributed/commands/utility_hook.h"
#include "distributed/deparser.h"
#include "distributed/listutils.h"
#include "distributed/metadata/dependency.h"
#include "distributed/metadata/distobject.h"
#include "distributed/metadata_sync.h"
#include "distributed/metadata_utility.h"
#include "distributed/multi_executor.h"
#include "distributed/relation_access_tracking.h"
#include "distributed/worker_create_or_replace.h"
#include "distributed/worker_manager.h"
#include "miscadmin.h"
static char * CreateCollationDDLInternal(Oid collationId, Oid *collowner,
@ -68,6 +66,8 @@ CreateCollationDDLInternal(Oid collationId, Oid *collowner, char **quotedCollati
char *collcollate;
char *collctype;
#if PG_VERSION_NUM >= PG_VERSION_15
/*
* In PG15, there is an added option to use ICU as global locale provider.
* pg_collation has three locale-related fields: collcollate and collctype,
@ -75,7 +75,7 @@ CreateCollationDDLInternal(Oid collationId, Oid *collowner, char **quotedCollati
* ICU-related field. Only the libc-related fields or the ICU-related field
* is set, never both.
*/
char *colllocale;
char *colliculocale;
bool isnull;
Datum datum = SysCacheGetAttr(COLLOID, heapTuple, Anum_pg_collation_collcollate,
@ -99,17 +99,27 @@ CreateCollationDDLInternal(Oid collationId, Oid *collowner, char **quotedCollati
collctype = NULL;
}
datum = SysCacheGetAttr(COLLOID, heapTuple, Anum_pg_collation_colllocale, &isnull);
datum = SysCacheGetAttr(COLLOID, heapTuple, Anum_pg_collation_colliculocale, &isnull);
if (!isnull)
{
colllocale = TextDatumGetCString(datum);
colliculocale = TextDatumGetCString(datum);
}
else
{
colllocale = NULL;
colliculocale = NULL;
}
Assert((collcollate && collctype) || colllocale);
AssertArg((collcollate && collctype) || colliculocale);
#else
/*
* In versions before 15, collcollate and collctype were type "name". Use
* pstrdup() to match the interface of 15 so that we consistently free the
* result later.
*/
collcollate = pstrdup(NameStr(collationForm->collcollate));
collctype = pstrdup(NameStr(collationForm->collctype));
#endif
if (collowner != NULL)
{
@ -120,7 +130,6 @@ CreateCollationDDLInternal(Oid collationId, Oid *collowner, char **quotedCollati
char *schemaName = get_namespace_name(collnamespace);
*quotedCollationName = quote_qualified_identifier(schemaName, collname);
const char *providerString =
collprovider == COLLPROVIDER_BUILTIN ? "builtin" :
collprovider == COLLPROVIDER_DEFAULT ? "default" :
collprovider == COLLPROVIDER_ICU ? "icu" :
collprovider == COLLPROVIDER_LIBC ? "libc" : NULL;
@ -135,12 +144,13 @@ CreateCollationDDLInternal(Oid collationId, Oid *collowner, char **quotedCollati
"CREATE COLLATION %s (provider = '%s'",
*quotedCollationName, providerString);
if (colllocale)
#if PG_VERSION_NUM >= PG_VERSION_15
if (colliculocale)
{
appendStringInfo(&collationNameDef,
", locale = %s",
quote_literal_cstr(colllocale));
pfree(colllocale);
quote_literal_cstr(colliculocale));
pfree(colliculocale);
}
else
{
@ -160,17 +170,25 @@ CreateCollationDDLInternal(Oid collationId, Oid *collowner, char **quotedCollati
pfree(collcollate);
pfree(collctype);
}
#if PG_VERSION_NUM >= PG_VERSION_16
char *collicurules = NULL;
datum = SysCacheGetAttr(COLLOID, heapTuple, Anum_pg_collation_collicurules, &isnull);
if (!isnull)
#else
if (strcmp(collcollate, collctype) == 0)
{
collicurules = TextDatumGetCString(datum);
appendStringInfo(&collationNameDef, ", rules = %s",
quote_literal_cstr(collicurules));
appendStringInfo(&collationNameDef,
", locale = %s",
quote_literal_cstr(collcollate));
}
else
{
appendStringInfo(&collationNameDef,
", lc_collate = %s, lc_ctype = %s",
quote_literal_cstr(collcollate),
quote_literal_cstr(collctype));
}
pfree(collcollate);
pfree(collctype);
#endif
if (!collisdeterministic)
{
appendStringInfoString(&collationNameDef, ", deterministic = false");

View File

@ -1,131 +0,0 @@
/*-------------------------------------------------------------------------
*
* comment.c
* Commands to interact with the comments for all database
* object types.
*
* Copyright (c) Citus Data, Inc.
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/genam.h"
#include "access/htup_details.h"
#include "access/table.h"
#include "catalog/pg_shdescription.h"
#include "nodes/parsenodes.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/rel.h"
#include "distributed/comment.h"
static char * GetCommentForObject(Oid classOid, Oid objectOid);
List *
GetCommentPropagationCommands(Oid classOid, Oid objOoid, char *objectName, ObjectType
objectType)
{
List *commands = NIL;
StringInfo commentStmt = makeStringInfo();
/* Get the comment for the database */
char *comment = GetCommentForObject(classOid, objOoid);
char const *commentObjectType = ObjectTypeNames[objectType];
/* Create the SQL command to propagate the comment to other nodes */
if (comment != NULL)
{
appendStringInfo(commentStmt, "COMMENT ON %s %s IS %s;", commentObjectType,
quote_identifier(objectName),
quote_literal_cstr(comment));
}
/* Add the command to the list */
if (commentStmt->len > 0)
{
commands = list_make1(commentStmt->data);
}
return commands;
}
static char *
GetCommentForObject(Oid classOid, Oid objectOid)
{
HeapTuple tuple;
char *comment = NULL;
/* Open pg_shdescription catalog */
Relation shdescRelation = table_open(SharedDescriptionRelationId, AccessShareLock);
/* Scan the table */
ScanKeyData scanKey[2];
ScanKeyInit(&scanKey[0],
Anum_pg_shdescription_objoid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(objectOid));
ScanKeyInit(&scanKey[1],
Anum_pg_shdescription_classoid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(classOid));
bool indexOk = true;
int scanKeyCount = 2;
SysScanDesc scan = systable_beginscan(shdescRelation, SharedDescriptionObjIndexId,
indexOk, NULL, scanKeyCount,
scanKey);
if ((tuple = systable_getnext(scan)) != NULL)
{
bool isNull = false;
TupleDesc tupdesc = RelationGetDescr(shdescRelation);
Datum descDatum = heap_getattr(tuple, Anum_pg_shdescription_description, tupdesc,
&isNull);
/* Add the command to the list */
if (!isNull)
{
comment = TextDatumGetCString(descDatum);
}
else
{
comment = NULL;
}
}
/* End the scan and close the catalog */
systable_endscan(scan);
table_close(shdescRelation, AccessShareLock);
return comment;
}
/*
* CommentObjectAddress resolves the ObjectAddress for the object
* on which the comment is placed. Optionally errors if the object does not
* exist based on the missing_ok flag passed in by the caller.
*/
List *
CommentObjectAddress(Node *node, bool missing_ok, bool isPostprocess)
{
CommentStmt *stmt = castNode(CommentStmt, node);
Relation relation;
ObjectAddress objectAddress = get_object_address(stmt->objtype, stmt->object,
&relation, AccessExclusiveLock,
missing_ok);
ObjectAddress *objectAddressCopy = palloc0(sizeof(ObjectAddress));
*objectAddressCopy = objectAddress;
return list_make1(objectAddressCopy);
}

View File

@ -23,9 +23,9 @@
#include "distributed/commands/utility_hook.h"
#include "distributed/deparser.h"
#include "distributed/listutils.h"
#include "distributed/metadata_sync.h"
#include "distributed/metadata/dependency.h"
#include "distributed/metadata/distobject.h"
#include "distributed/metadata_sync.h"
#include "distributed/multi_executor.h"
#include "distributed/worker_transaction.h"
@ -235,7 +235,7 @@ PreprocessDropDistributedObjectStmt(Node *node, const char *queryString,
List *distributedObjects = NIL;
List *distributedObjectAddresses = NIL;
Node *object = NULL;
foreach_declared_ptr(object, stmt->objects)
foreach_ptr(object, stmt->objects)
{
/* TODO understand if the lock should be sth else */
Relation rel = NULL; /* not used, but required to pass to get_object_address */
@ -267,7 +267,7 @@ PreprocessDropDistributedObjectStmt(Node *node, const char *queryString,
* remove the entries for the distributed objects on dropping
*/
ObjectAddress *address = NULL;
foreach_declared_ptr(address, distributedObjectAddresses)
foreach_ptr(address, distributedObjectAddresses)
{
UnmarkObjectDistributed(address);
}
@ -303,7 +303,7 @@ DropTextSearchDictObjectAddress(Node *node, bool missing_ok, bool isPostprocess)
List *objectAddresses = NIL;
List *objNameList = NIL;
foreach_declared_ptr(objNameList, stmt->objects)
foreach_ptr(objNameList, stmt->objects)
{
Oid tsdictOid = get_ts_dict_oid(objNameList, missing_ok);
@ -328,7 +328,7 @@ DropTextSearchConfigObjectAddress(Node *node, bool missing_ok, bool isPostproces
List *objectAddresses = NIL;
List *objNameList = NIL;
foreach_declared_ptr(objNameList, stmt->objects)
foreach_ptr(objNameList, stmt->objects)
{
Oid tsconfigOid = get_ts_config_oid(objNameList, missing_ok);

File diff suppressed because it is too large Load Diff

View File

@ -11,99 +11,31 @@
#include "postgres.h"
#include "miscadmin.h"
#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/table.h"
#include "access/xact.h"
#include "catalog/objectaddress.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_database.h"
#include "catalog/pg_database_d.h"
#include "catalog/pg_tablespace.h"
#include "commands/dbcommands.h"
#include "commands/defrem.h"
#include "miscadmin.h"
#include "nodes/parsenodes.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/relcache.h"
#include "utils/syscache.h"
#include "distributed/adaptive_executor.h"
#include "distributed/commands.h"
#include "distributed/commands/serialize_distributed_ddls.h"
#include "distributed/commands/utility_hook.h"
#include "distributed/comment.h"
#include "distributed/deparse_shard_query.h"
#include "distributed/deparser.h"
#include "distributed/listutils.h"
#include "distributed/local_executor.h"
#include "distributed/metadata/distobject.h"
#include "distributed/metadata_sync.h"
#include "distributed/metadata_utility.h"
#include "distributed/multi_executor.h"
#include "distributed/relation_access_tracking.h"
#include "distributed/shard_cleaner.h"
#include "distributed/worker_protocol.h"
#include "distributed/worker_transaction.h"
/*
* Used to save original name of the database before it is replaced with a
* temporary name for failure handling purposes in PreprocessCreateDatabaseStmt().
*/
static char *CreateDatabaseCommandOriginalDbName = NULL;
/*
* The format string used when creating a temporary databases for failure
* handling purposes.
*
* The fields are as follows to ensure using a unique name for each temporary
* database:
* - operationId: The operation id returned by RegisterOperationNeedingCleanup().
* - groupId: The group id of the worker node where CREATE DATABASE command
* is issued from.
*/
#define TEMP_DATABASE_NAME_FMT "citus_temp_database_%lu_%d"
/*
* DatabaseCollationInfo is used to store collation related information of a database.
*/
typedef struct DatabaseCollationInfo
{
char *datcollate;
char *datctype;
char *daticulocale;
char *datcollversion;
#if PG_VERSION_NUM >= PG_VERSION_16
char *daticurules;
#endif
} DatabaseCollationInfo;
static char * GenerateCreateDatabaseStatementFromPgDatabase(Form_pg_database
databaseForm);
static DatabaseCollationInfo GetDatabaseCollation(Oid dbOid);
static AlterOwnerStmt * RecreateAlterDatabaseOwnerStmt(Oid databaseOid);
static char * GetLocaleProviderString(char datlocprovider);
static char * GetTablespaceName(Oid tablespaceOid);
static ObjectAddress * GetDatabaseAddressFromDatabaseName(char *databaseName,
bool missingOk);
static List * FilterDistributedDatabases(List *databases);
static Oid get_database_owner(Oid dbId);
static Oid get_database_owner(Oid db_oid);
/* controlled via GUC */
bool EnableCreateDatabasePropagation = false;
bool EnableAlterDatabaseOwner = true;
/*
* AlterDatabaseOwnerObjectAddress returns the ObjectAddress of the database that is the
* object of the AlterOwnerStmt. Errors if missing_ok is false.
@ -160,13 +92,13 @@ RecreateAlterDatabaseOwnerStmt(Oid databaseOid)
* get_database_owner returns the Oid of the role owning the database
*/
static Oid
get_database_owner(Oid dbId)
get_database_owner(Oid db_oid)
{
HeapTuple tuple = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(dbId));
HeapTuple tuple = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(db_oid));
if (!HeapTupleIsValid(tuple))
{
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_DATABASE),
errmsg("database with OID %u does not exist", dbId)));
errmsg("database with OID %u does not exist", db_oid)));
}
Oid dba = ((Form_pg_database) GETSTRUCT(tuple))->datdba;
@ -175,826 +107,3 @@ get_database_owner(Oid dbId)
return dba;
}
/*
* PreprocessGrantOnDatabaseStmt is executed before the statement is applied to the local
* postgres instance.
*
* In this stage we can prepare the commands that need to be run on all workers to grant
* on databases.
*/
List *
PreprocessGrantOnDatabaseStmt(Node *node, const char *queryString,
ProcessUtilityContext processUtilityContext)
{
if (!ShouldPropagate())
{
return NIL;
}
GrantStmt *stmt = castNode(GrantStmt, node);
Assert(stmt->objtype == OBJECT_DATABASE);
List *distributedDatabases = FilterDistributedDatabases(stmt->objects);
if (list_length(distributedDatabases) == 0)
{
return NIL;
}
EnsureCoordinator();
List *originalObjects = stmt->objects;
stmt->objects = distributedDatabases;
char *sql = DeparseTreeNode((Node *) stmt);
stmt->objects = originalObjects;
List *commands = list_make3(DISABLE_DDL_PROPAGATION,
(void *) sql,
ENABLE_DDL_PROPAGATION);
return NodeDDLTaskList(NON_COORDINATOR_NODES, commands);
}
/*
* FilterDistributedDatabases filters the database list and returns the distributed ones,
* as a list.
*/
static List *
FilterDistributedDatabases(List *databases)
{
List *distributedDatabases = NIL;
String *databaseName = NULL;
foreach_declared_ptr(databaseName, databases)
{
bool missingOk = true;
ObjectAddress *dbAddress =
GetDatabaseAddressFromDatabaseName(strVal(databaseName), missingOk);
if (IsAnyObjectDistributed(list_make1(dbAddress)))
{
distributedDatabases = lappend(distributedDatabases, databaseName);
}
}
return distributedDatabases;
}
/*
* IsSetTablespaceStatement returns true if the statement is a SET TABLESPACE statement,
* false otherwise.
*/
static bool
IsSetTablespaceStatement(AlterDatabaseStmt *stmt)
{
DefElem *def = NULL;
foreach_declared_ptr(def, stmt->options)
{
if (strcmp(def->defname, "tablespace") == 0)
{
return true;
}
}
return false;
}
/*
* PreprocessAlterDatabaseStmt is executed before the statement is applied to the local
* postgres instance.
*
* In this stage we can prepare the commands that need to be run on all workers to grant
* on databases.
*
* We also serialize database commands globally by acquiring a Citus specific advisory
* lock based on OCLASS_DATABASE on the first primary worker node.
*/
List *
PreprocessAlterDatabaseStmt(Node *node, const char *queryString,
ProcessUtilityContext processUtilityContext)
{
bool missingOk = false;
AlterDatabaseStmt *stmt = castNode(AlterDatabaseStmt, node);
ObjectAddress *dbAddress = GetDatabaseAddressFromDatabaseName(stmt->dbname,
missingOk);
if (!ShouldPropagate() || !IsAnyObjectDistributed(list_make1(dbAddress)))
{
return NIL;
}
EnsureCoordinator();
SerializeDistributedDDLsOnObjectClassObject(OCLASS_DATABASE, stmt->dbname);
char *sql = DeparseTreeNode((Node *) stmt);
List *commands = list_make3(DISABLE_DDL_PROPAGATION,
sql,
ENABLE_DDL_PROPAGATION);
if (IsSetTablespaceStatement(stmt))
{
/*
* Set tablespace does not work inside a transaction.Therefore, we need to use
* NontransactionalNodeDDLTask to run the command on the workers outside
* the transaction block.
*/
bool warnForPartialFailure = true;
return NontransactionalNodeDDLTaskList(NON_COORDINATOR_NODES, commands,
warnForPartialFailure);
}
else
{
return NodeDDLTaskList(NON_COORDINATOR_NODES, commands);
}
}
/*
* PreprocessAlterDatabaseRefreshCollStmt is executed before the statement is applied to
* the local postgres instance.
*
* In this stage we can prepare the commands that need to be run on all workers to grant
* on databases.
*
* We also serialize database commands globally by acquiring a Citus specific advisory
* lock based on OCLASS_DATABASE on the first primary worker node.
*/
List *
PreprocessAlterDatabaseRefreshCollStmt(Node *node, const char *queryString,
ProcessUtilityContext processUtilityContext)
{
bool missingOk = true;
AlterDatabaseRefreshCollStmt *stmt = castNode(AlterDatabaseRefreshCollStmt, node);
ObjectAddress *dbAddress = GetDatabaseAddressFromDatabaseName(stmt->dbname,
missingOk);
if (!ShouldPropagate() || !IsAnyObjectDistributed(list_make1(dbAddress)))
{
return NIL;
}
EnsureCoordinator();
SerializeDistributedDDLsOnObjectClassObject(OCLASS_DATABASE, stmt->dbname);
char *sql = DeparseTreeNode((Node *) stmt);
List *commands = list_make3(DISABLE_DDL_PROPAGATION,
(void *) sql,
ENABLE_DDL_PROPAGATION);
return NodeDDLTaskList(NON_COORDINATOR_NODES, commands);
}
/*
* PreprocessAlterDatabaseRenameStmt is executed before the statement is applied to
* the local postgres instance.
*
* We also serialize database commands globally by acquiring a Citus specific advisory
* lock based on OCLASS_DATABASE on the first primary worker node.
*
* We acquire this lock here instead of PostprocessAlterDatabaseRenameStmt because the
* command renames the database and SerializeDistributedDDLsOnObjectClass resolves the
* object on workers based on database name. For this reason, we need to acquire the lock
* before the command is applied to the local postgres instance.
*/
List *
PreprocessAlterDatabaseRenameStmt(Node *node, const char *queryString,
ProcessUtilityContext processUtilityContext)
{
bool missingOk = true;
RenameStmt *stmt = castNode(RenameStmt, node);
ObjectAddress *dbAddress = GetDatabaseAddressFromDatabaseName(stmt->subname,
missingOk);
if (!ShouldPropagate() || !IsAnyObjectDistributed(list_make1(dbAddress)))
{
return NIL;
}
EnsureCoordinator();
/*
* Different than other ALTER DATABASE commands, we first acquire a lock
* by providing InvalidOid because we want ALTER TABLE .. RENAME TO ..
* commands to block not only with ALTER DATABASE operations but also
* with CREATE DATABASE operations because they might cause name conflicts
* and that could also cause deadlocks too.
*/
SerializeDistributedDDLsOnObjectClass(OCLASS_DATABASE);
SerializeDistributedDDLsOnObjectClassObject(OCLASS_DATABASE, stmt->subname);
return NIL;
}
/*
* PostprocessAlterDatabaseRenameStmt is executed after the statement is applied to the local
* postgres instance. In this stage we prepare ALTER DATABASE RENAME statement to be run on
* all workers.
*/
List *
PostprocessAlterDatabaseRenameStmt(Node *node, const char *queryString)
{
bool missingOk = false;
RenameStmt *stmt = castNode(RenameStmt, node);
ObjectAddress *dbAddress = GetDatabaseAddressFromDatabaseName(stmt->newname,
missingOk);
if (!ShouldPropagate() || !IsAnyObjectDistributed(list_make1(dbAddress)))
{
return NIL;
}
EnsureCoordinator();
char *sql = DeparseTreeNode((Node *) stmt);
List *commands = list_make3(DISABLE_DDL_PROPAGATION,
(void *) sql,
ENABLE_DDL_PROPAGATION);
return NodeDDLTaskList(NON_COORDINATOR_NODES, commands);
}
/*
* PreprocessAlterDatabaseSetStmt is executed before the statement is applied to the local
* postgres instance.
*
* In this stage we can prepare the commands that need to be run on all workers to grant
* on databases.
*
* We also serialize database commands globally by acquiring a Citus specific advisory
* lock based on OCLASS_DATABASE on the first primary worker node.
*/
List *
PreprocessAlterDatabaseSetStmt(Node *node, const char *queryString,
ProcessUtilityContext processUtilityContext)
{
AlterDatabaseSetStmt *stmt = castNode(AlterDatabaseSetStmt, node);
bool missingOk = true;
ObjectAddress *dbAddress = GetDatabaseAddressFromDatabaseName(stmt->dbname,
missingOk);
if (!ShouldPropagate() || !IsAnyObjectDistributed(list_make1(dbAddress)))
{
return NIL;
}
EnsureCoordinator();
SerializeDistributedDDLsOnObjectClassObject(OCLASS_DATABASE, stmt->dbname);
char *sql = DeparseTreeNode((Node *) stmt);
List *commands = list_make3(DISABLE_DDL_PROPAGATION,
(void *) sql,
ENABLE_DDL_PROPAGATION);
return NodeDDLTaskList(NON_COORDINATOR_NODES, commands);
}
/*
* PreprocessCreateDatabaseStmt is executed before the statement is applied to the local
* Postgres instance.
*
* In this stage, we perform validations that we want to ensure before delegating to
* previous utility hooks because it might not be convenient to throw an error in an
* implicit transaction that creates a database. Also in this stage, we save the original
* database name and replace dbname field with a temporary name for failure handling
* purposes. We let Postgres create the database with the temporary name, insert a cleanup
* record for the temporary database name on all nodes and let PostprocessCreateDatabaseStmt()
* to return the distributed DDL job that both creates the database with the temporary name
* and then renames it back to its original name.
*
* We also serialize database commands globally by acquiring a Citus specific advisory
* lock based on OCLASS_DATABASE on the first primary worker node.
*/
List *
PreprocessCreateDatabaseStmt(Node *node, const char *queryString,
ProcessUtilityContext processUtilityContext)
{
if (!EnableCreateDatabasePropagation || !ShouldPropagate())
{
return NIL;
}
EnsureCoordinatorIsInMetadata();
CreatedbStmt *stmt = castNode(CreatedbStmt, node);
EnsureSupportedCreateDatabaseCommand(stmt);
SerializeDistributedDDLsOnObjectClass(OCLASS_DATABASE);
OperationId operationId = RegisterOperationNeedingCleanup();
char *tempDatabaseName = psprintf(TEMP_DATABASE_NAME_FMT,
operationId, GetLocalGroupId());
List *remoteNodes = TargetWorkerSetNodeList(ALL_SHARD_NODES, RowShareLock);
WorkerNode *remoteNode = NULL;
foreach_declared_ptr(remoteNode, remoteNodes)
{
InsertCleanupRecordOutsideTransaction(
CLEANUP_OBJECT_DATABASE,
pstrdup(quote_identifier(tempDatabaseName)),
remoteNode->groupId,
CLEANUP_ON_FAILURE
);
}
CreateDatabaseCommandOriginalDbName = stmt->dbname;
stmt->dbname = tempDatabaseName;
/*
* Delete cleanup records in the same transaction so that if the current
* transactions fails for some reason, then the cleanup records won't be
* deleted. In the happy path, we will delete the cleanup records without
* deferring them to the background worker.
*/
FinalizeOperationNeedingCleanupOnSuccess("create database");
return NIL;
}
/*
* PostprocessCreateDatabaseStmt is executed after the statement is applied to the local
* postgres instance.
*
* In this stage, we first rename the temporary database back to its original name for
* local node and then return a list of distributed DDL jobs to create the database with
* the temporary name and then to rename it back to its original name. That way, if CREATE
* DATABASE fails on any of the nodes, the temporary database will be cleaned up by the
* cleanup records that we inserted in PreprocessCreateDatabaseStmt() and in case of a
* failure, we won't leak any databases called as the name that user intended to use for
* the database.
*/
List *
PostprocessCreateDatabaseStmt(Node *node, const char *queryString)
{
if (!EnableCreateDatabasePropagation || !ShouldPropagate())
{
return NIL;
}
EnsurePropagationToCoordinator();
/*
* Given that CREATE DATABASE doesn't support "IF NOT EXISTS" and we're
* in the post-process, database must exist, hence missingOk = false.
*/
bool missingOk = false;
bool isPostProcess = true;
List *addresses = GetObjectAddressListFromParseTree(node, missingOk,
isPostProcess);
EnsureAllObjectDependenciesExistOnAllNodes(addresses);
char *createDatabaseCommand = DeparseTreeNode(node);
List *createDatabaseCommands = list_make3(DISABLE_DDL_PROPAGATION,
(void *) createDatabaseCommand,
ENABLE_DDL_PROPAGATION);
/*
* Since the CREATE DATABASE statements cannot be executed in a transaction
* block, we need to use NontransactionalNodeDDLTaskList() to send the CREATE
* DATABASE statement to the workers.
*/
bool warnForPartialFailure = false;
List *createDatabaseDDLJobList =
NontransactionalNodeDDLTaskList(REMOTE_NODES, createDatabaseCommands,
warnForPartialFailure);
CreatedbStmt *stmt = castNode(CreatedbStmt, node);
char *renameDatabaseCommand =
psprintf("ALTER DATABASE %s RENAME TO %s",
quote_identifier(stmt->dbname),
quote_identifier(CreateDatabaseCommandOriginalDbName));
List *renameDatabaseCommands = list_make3(DISABLE_DDL_PROPAGATION,
renameDatabaseCommand,
ENABLE_DDL_PROPAGATION);
/*
* We use NodeDDLTaskList() to send the RENAME DATABASE statement to the
* workers because we want to execute it in a coordinated transaction.
*/
List *renameDatabaseDDLJobList =
NodeDDLTaskList(REMOTE_NODES, renameDatabaseCommands);
/*
* Temporarily disable citus.enable_ddl_propagation before issuing
* rename command locally because we don't want to execute it on remote
* nodes yet. We will execute it on remote nodes by returning it as a
* distributed DDL job.
*
* The reason why we don't want to execute it on remote nodes yet is that
* the database is not created on remote nodes yet.
*/
int saveNestLevel = NewGUCNestLevel();
set_config_option("citus.enable_ddl_propagation", "off",
(superuser() ? PGC_SUSET : PGC_USERSET), PGC_S_SESSION,
GUC_ACTION_LOCAL, true, 0, false);
ExecuteUtilityCommand(renameDatabaseCommand);
AtEOXact_GUC(true, saveNestLevel);
/*
* Restore the original database name because MarkObjectDistributed()
* resolves oid of the object based on the database name and is called
* after executing the distributed DDL job that renames temporary database.
*/
stmt->dbname = CreateDatabaseCommandOriginalDbName;
return list_concat(createDatabaseDDLJobList, renameDatabaseDDLJobList);
}
/*
* PreprocessDropDatabaseStmt is executed before the statement is applied to the local
* postgres instance. In this stage we can prepare the commands that need to be run on
* all workers to drop the database.
*
* We also serialize database commands globally by acquiring a Citus specific advisory
* lock based on OCLASS_DATABASE on the first primary worker node.
*/
List *
PreprocessDropDatabaseStmt(Node *node, const char *queryString,
ProcessUtilityContext processUtilityContext)
{
if (!EnableCreateDatabasePropagation || !ShouldPropagate())
{
return NIL;
}
EnsurePropagationToCoordinator();
DropdbStmt *stmt = (DropdbStmt *) node;
bool isPostProcess = false;
List *addresses = GetObjectAddressListFromParseTree(node, stmt->missing_ok,
isPostProcess);
if (list_length(addresses) != 1)
{
ereport(ERROR, (errmsg("unexpected number of objects found when "
"executing DROP DATABASE command")));
}
ObjectAddress *address = (ObjectAddress *) linitial(addresses);
if (address->objectId == InvalidOid || !IsAnyObjectDistributed(list_make1(address)))
{
return NIL;
}
SerializeDistributedDDLsOnObjectClassObject(OCLASS_DATABASE, stmt->dbname);
char *dropDatabaseCommand = DeparseTreeNode(node);
List *dropDatabaseCommands = list_make3(DISABLE_DDL_PROPAGATION,
(void *) dropDatabaseCommand,
ENABLE_DDL_PROPAGATION);
/*
* Due to same reason stated in PostprocessCreateDatabaseStmt(), we need to
* use NontransactionalNodeDDLTaskList() to send the DROP DATABASE statement
* to the workers.
*/
bool warnForPartialFailure = true;
List *dropDatabaseDDLJobList =
NontransactionalNodeDDLTaskList(REMOTE_NODES, dropDatabaseCommands,
warnForPartialFailure);
return dropDatabaseDDLJobList;
}
/*
* DropDatabaseStmtObjectAddress gets the ObjectAddress of the database that is the
* object of the DropdbStmt.
*/
List *
DropDatabaseStmtObjectAddress(Node *node, bool missingOk, bool isPostprocess)
{
DropdbStmt *stmt = castNode(DropdbStmt, node);
ObjectAddress *dbAddress = GetDatabaseAddressFromDatabaseName(stmt->dbname,
missingOk);
return list_make1(dbAddress);
}
/*
* CreateDatabaseStmtObjectAddress gets the ObjectAddress of the database that is the
* object of the CreatedbStmt.
*/
List *
CreateDatabaseStmtObjectAddress(Node *node, bool missingOk, bool isPostprocess)
{
CreatedbStmt *stmt = castNode(CreatedbStmt, node);
ObjectAddress *dbAddress = GetDatabaseAddressFromDatabaseName(stmt->dbname,
missingOk);
return list_make1(dbAddress);
}
/*
* EnsureSupportedCreateDatabaseCommand validates the options provided for the CREATE
* DATABASE command.
*
* Parameters:
* stmt: A CreatedbStmt struct representing a CREATE DATABASE command.
* The options field is a list of DefElem structs, each representing an option.
*
* Currently, this function checks for the following:
* - The "oid" option is not supported.
* - The "template" option is only supported with the value "template1".
* - The "strategy" option is only supported with the value "wal_log".
*/
void
EnsureSupportedCreateDatabaseCommand(CreatedbStmt *stmt)
{
DefElem *option = NULL;
foreach_declared_ptr(option, stmt->options)
{
if (strcmp(option->defname, "oid") == 0)
{
ereport(ERROR,
errmsg("CREATE DATABASE option \"%s\" is not supported",
option->defname));
}
char *optionValue = defGetString(option);
if (strcmp(option->defname, "template") == 0 &&
strcmp(optionValue, "template1") != 0)
{
ereport(ERROR, errmsg("Only template1 is supported as template "
"parameter for CREATE DATABASE"));
}
if (strcmp(option->defname, "strategy") == 0 &&
strcmp(optionValue, "wal_log") != 0)
{
ereport(ERROR, errmsg("Only wal_log is supported as strategy "
"parameter for CREATE DATABASE"));
}
}
}
/*
* GetDatabaseAddressFromDatabaseName gets the database name and returns the ObjectAddress
* of the database.
*/
static ObjectAddress *
GetDatabaseAddressFromDatabaseName(char *databaseName, bool missingOk)
{
Oid databaseOid = get_database_oid(databaseName, missingOk);
ObjectAddress *dbObjectAddress = palloc0(sizeof(ObjectAddress));
ObjectAddressSet(*dbObjectAddress, DatabaseRelationId, databaseOid);
return dbObjectAddress;
}
/*
* GetTablespaceName gets the tablespace oid and returns the tablespace name.
*/
static char *
GetTablespaceName(Oid tablespaceOid)
{
HeapTuple tuple = SearchSysCache1(TABLESPACEOID, ObjectIdGetDatum(tablespaceOid));
if (!HeapTupleIsValid(tuple))
{
return NULL;
}
Form_pg_tablespace tablespaceForm = (Form_pg_tablespace) GETSTRUCT(tuple);
char *tablespaceName = pstrdup(NameStr(tablespaceForm->spcname));
ReleaseSysCache(tuple);
return tablespaceName;
}
/*
* GetDatabaseMetadataSyncCommands returns a list of sql statements
* for the given database id. The list contains the database ddl command,
* grant commands and comment propagation commands.
*/
List *
GetDatabaseMetadataSyncCommands(Oid dbOid)
{
char *databaseName = get_database_name(dbOid);
char *databaseDDLCommand = CreateDatabaseDDLCommand(dbOid);
List *ddlCommands = list_make1(databaseDDLCommand);
List *grantDDLCommands = GrantOnDatabaseDDLCommands(dbOid);
List *commentDDLCommands = GetCommentPropagationCommands(DatabaseRelationId, dbOid,
databaseName,
OBJECT_DATABASE);
ddlCommands = list_concat(ddlCommands, grantDDLCommands);
ddlCommands = list_concat(ddlCommands, commentDDLCommands);
return ddlCommands;
}
/*
* GetDatabaseCollation gets oid of a database and returns all the collation related information
* We need this method since collation related info in Form_pg_database is not accessible.
*/
static DatabaseCollationInfo
GetDatabaseCollation(Oid dbOid)
{
DatabaseCollationInfo info;
memset(&info, 0, sizeof(DatabaseCollationInfo));
Relation rel = table_open(DatabaseRelationId, AccessShareLock);
HeapTuple tup = get_catalog_object_by_oid(rel, Anum_pg_database_oid, dbOid);
if (!HeapTupleIsValid(tup))
{
elog(ERROR, "cache lookup failed for database %u", dbOid);
}
bool isNull = false;
TupleDesc tupdesc = RelationGetDescr(rel);
Datum collationDatum = heap_getattr(tup, Anum_pg_database_datcollate, tupdesc,
&isNull);
info.datcollate = TextDatumGetCString(collationDatum);
Datum ctypeDatum = heap_getattr(tup, Anum_pg_database_datctype, tupdesc, &isNull);
info.datctype = TextDatumGetCString(ctypeDatum);
Datum icuLocaleDatum = heap_getattr(tup, Anum_pg_database_datlocale, tupdesc,
&isNull);
if (!isNull)
{
info.daticulocale = TextDatumGetCString(icuLocaleDatum);
}
Datum collverDatum = heap_getattr(tup, Anum_pg_database_datcollversion, tupdesc,
&isNull);
if (!isNull)
{
info.datcollversion = TextDatumGetCString(collverDatum);
}
#if PG_VERSION_NUM >= PG_VERSION_16
Datum icurulesDatum = heap_getattr(tup, Anum_pg_database_daticurules, tupdesc,
&isNull);
if (!isNull)
{
info.daticurules = TextDatumGetCString(icurulesDatum);
}
#endif
table_close(rel, AccessShareLock);
heap_freetuple(tup);
return info;
}
/*
* GetLocaleProviderString gets the datlocprovider stored in pg_database
* and returns the string representation of the datlocprovider
*/
static char *
GetLocaleProviderString(char datlocprovider)
{
switch (datlocprovider)
{
case 'c':
{
return "libc";
}
case 'i':
{
return "icu";
}
default:
{
ereport(ERROR, (errmsg("unexpected datlocprovider value: %c",
datlocprovider)));
}
}
}
/*
* GenerateCreateDatabaseStatementFromPgDatabase gets the pg_database tuple and returns the
* CREATE DATABASE statement that can be used to create given database.
*
* Note that this doesn't deparse OID of the database and this is not a
* problem as we anyway don't allow specifying custom OIDs for databases
* when creating them.
*/
static char *
GenerateCreateDatabaseStatementFromPgDatabase(Form_pg_database databaseForm)
{
DatabaseCollationInfo collInfo = GetDatabaseCollation(databaseForm->oid);
StringInfoData str;
initStringInfo(&str);
appendStringInfo(&str, "CREATE DATABASE %s",
quote_identifier(NameStr(databaseForm->datname)));
appendStringInfo(&str, " CONNECTION LIMIT %d", databaseForm->datconnlimit);
appendStringInfo(&str, " ALLOW_CONNECTIONS = %s",
quote_literal_cstr(databaseForm->datallowconn ? "true" : "false"));
appendStringInfo(&str, " IS_TEMPLATE = %s",
quote_literal_cstr(databaseForm->datistemplate ? "true" : "false"));
appendStringInfo(&str, " LC_COLLATE = %s",
quote_literal_cstr(collInfo.datcollate));
appendStringInfo(&str, " LC_CTYPE = %s", quote_literal_cstr(collInfo.datctype));
appendStringInfo(&str, " OWNER = %s",
quote_identifier(GetUserNameFromId(databaseForm->datdba, false)));
appendStringInfo(&str, " TABLESPACE = %s",
quote_identifier(GetTablespaceName(databaseForm->dattablespace)));
appendStringInfo(&str, " ENCODING = %s",
quote_literal_cstr(pg_encoding_to_char(databaseForm->encoding)));
if (collInfo.datcollversion != NULL)
{
appendStringInfo(&str, " COLLATION_VERSION = %s",
quote_identifier(collInfo.datcollversion));
}
if (collInfo.daticulocale != NULL)
{
appendStringInfo(&str, " ICU_LOCALE = %s", quote_identifier(
collInfo.daticulocale));
}
appendStringInfo(&str, " LOCALE_PROVIDER = %s",
quote_identifier(GetLocaleProviderString(
databaseForm->datlocprovider)));
#if PG_VERSION_NUM >= PG_VERSION_16
if (collInfo.daticurules != NULL)
{
appendStringInfo(&str, " ICU_RULES = %s", quote_identifier(
collInfo.daticurules));
}
#endif
return str.data;
}
/*
* CreateDatabaseDDLCommand returns a CREATE DATABASE command to create given
* database
*
* Command is wrapped by citus_internal_database_command() UDF
* to avoid from transaction block restrictions that apply to database commands.
*/
char *
CreateDatabaseDDLCommand(Oid dbId)
{
HeapTuple tuple = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(dbId));
if (!HeapTupleIsValid(tuple))
{
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_DATABASE),
errmsg("database with OID %u does not exist", dbId)));
}
Form_pg_database databaseForm = (Form_pg_database) GETSTRUCT(tuple);
char *createStmt = GenerateCreateDatabaseStatementFromPgDatabase(databaseForm);
StringInfo outerDbStmt = makeStringInfo();
/* Generate the CREATE DATABASE statement */
appendStringInfo(outerDbStmt,
"SELECT citus_internal.database_command(%s)",
quote_literal_cstr(createStmt));
ReleaseSysCache(tuple);
return outerDbStmt->data;
}

View File

@ -10,14 +10,9 @@
#include "postgres.h"
#include "miscadmin.h"
#include "catalog/dependency.h"
#include "catalog/objectaddress.h"
#include "commands/extension.h"
#include "storage/lmgr.h"
#include "utils/lsyscache.h"
#include "distributed/commands.h"
#include "distributed/commands/utility_hook.h"
#include "distributed/connection_management.h"
@ -30,147 +25,57 @@
#include "distributed/remote_commands.h"
#include "distributed/worker_manager.h"
#include "distributed/worker_transaction.h"
typedef enum RequiredObjectSet
{
REQUIRE_ONLY_DEPENDENCIES = 1,
REQUIRE_OBJECT_AND_DEPENDENCIES = 2,
} RequiredObjectSet;
#include "miscadmin.h"
#include "storage/lmgr.h"
#include "utils/lsyscache.h"
static void EnsureDependenciesCanBeDistributed(const ObjectAddress *relationAddress);
static void ErrorIfCircularDependencyExists(const ObjectAddress *objectAddress);
static int ObjectAddressComparator(const void *a, const void *b);
static void EnsureDependenciesExistOnAllNodes(const ObjectAddress *target);
static void EnsureRequiredObjectSetExistOnAllNodes(const ObjectAddress *target,
RequiredObjectSet requiredObjectSet);
static List * GetDependencyCreateDDLCommands(const ObjectAddress *dependency);
static bool ShouldPropagateObject(const ObjectAddress *address);
static char * DropTableIfExistsCommand(Oid relationId);
/*
* EnsureObjectAndDependenciesExistOnAllNodes is a wrapper around
* EnsureRequiredObjectSetExistOnAllNodes to ensure the "object itself" (together
* with its dependencies) is available on all nodes.
*
* Different than EnsureDependenciesExistOnAllNodes, we return early if the
* target object is distributed already.
*
* The reason why we don't do the same in EnsureDependenciesExistOnAllNodes
* is that it's is used when altering an object too and hence the target object
* may instantly have a dependency that needs to be propagated now. For example,
* when "GRANT non_dist_role TO dist_role" is executed, we need to propagate
* "non_dist_role" to all nodes before propagating the "GRANT" command itself.
* For this reason, we call EnsureDependenciesExistOnAllNodes for "dist_role"
* and it would automatically discover that "non_dist_role" is a dependency of
* "dist_role" and propagate it beforehand.
*
* However, when we're requested to create the target object itself (and
* implicitly its dependencies), we're sure that we're not altering the target
* object itself, hence we can return early if the target object is already
* distributed. This is the case, for example, when
* "REASSIGN OWNED BY dist_role TO non_dist_role" is executed. In that case,
* "non_dist_role" is not a dependency of "dist_role" but we want to distribute
* "non_dist_role" beforehand and we call this function for "non_dist_role",
* not for "dist_role".
*
* See EnsureRequiredObjectExistOnAllNodes to learn more about how this
* function deals with an object created within the same transaction.
*/
void
EnsureObjectAndDependenciesExistOnAllNodes(const ObjectAddress *target)
{
if (IsAnyObjectDistributed(list_make1((ObjectAddress *) target)))
{
return;
}
EnsureRequiredObjectSetExistOnAllNodes(target, REQUIRE_OBJECT_AND_DEPENDENCIES);
}
/*
* EnsureDependenciesExistOnAllNodes is a wrapper around
* EnsureRequiredObjectSetExistOnAllNodes to ensure "all dependencies" of given
* object --but not the object itself-- are available on all nodes.
*
* See EnsureRequiredObjectSetExistOnAllNodes to learn more about how this
* function deals with an object created within the same transaction.
*/
static void
EnsureDependenciesExistOnAllNodes(const ObjectAddress *target)
{
EnsureRequiredObjectSetExistOnAllNodes(target, REQUIRE_ONLY_DEPENDENCIES);
}
/*
* EnsureRequiredObjectSetExistOnAllNodes finds all the dependencies that we support and makes
* sure these are available on all nodes if required object set is REQUIRE_ONLY_DEPENDENCIES.
* Otherwise, i.e., if required object set is REQUIRE_OBJECT_AND_DEPENDENCIES, then this
* function creates the object itself on all nodes too. This function ensures that each
* of the dependencies are supported by Citus but doesn't check the same for the target
* object itself (when REQUIRE_OBJECT_AND_DEPENDENCIES) is provided because we assume that
* callers don't call this function for an unsupported function at all.
*
* If not available, they will be created on the nodes via a separate session that will be
* committed directly so that the objects are visible to potentially multiple sessions creating
* the shards.
* EnsureDependenciesExistOnAllNodes finds all the dependencies that we support and makes
* sure these are available on all workers. If not available they will be created on the
* workers via a separate session that will be committed directly so that the objects are
* visible to potentially multiple sessions creating the shards.
*
* Note; only the actual objects are created via a separate session, the records to
* pg_dist_object are created in this session. As a side effect the objects could be
* created on the nodes without a catalog entry. Updates to the objects on local node
* are not propagated to the remote nodes until the record is visible on local node.
* created on the workers without a catalog entry. Updates to the objects on the coordinator
* are not propagated to the workers until the record is visible on the coordinator.
*
* This is solved by creating the dependencies in an idempotent manner, either via
* postgres native CREATE IF NOT EXISTS, or citus helper functions.
*/
static void
EnsureRequiredObjectSetExistOnAllNodes(const ObjectAddress *target,
RequiredObjectSet requiredObjectSet)
EnsureDependenciesExistOnAllNodes(const ObjectAddress *target)
{
Assert(requiredObjectSet == REQUIRE_ONLY_DEPENDENCIES ||
requiredObjectSet == REQUIRE_OBJECT_AND_DEPENDENCIES);
List *objectsWithCommands = NIL;
List *dependenciesWithCommands = NIL;
List *ddlCommands = NULL;
/*
* If there is any unsupported dependency or circular dependency exists, Citus can
* not ensure dependencies will exist on all nodes.
*
* Note that we don't check whether "target" is distributable (in case
* REQUIRE_OBJECT_AND_DEPENDENCIES is provided) because we expect callers
* to not even call this function if Citus doesn't know how to propagate
* "target" object itself.
*/
EnsureDependenciesCanBeDistributed(target);
/* collect all dependencies in creation order and get their ddl commands */
List *objectsToBeCreated = GetDependenciesForObject(target);
/*
* Append the target object to make sure that it's created after its
* dependencies are created, if requested.
*/
if (requiredObjectSet == REQUIRE_OBJECT_AND_DEPENDENCIES)
List *dependencies = GetDependenciesForObject(target);
ObjectAddress *dependency = NULL;
foreach_ptr(dependency, dependencies)
{
ObjectAddress *targetCopy = palloc(sizeof(ObjectAddress));
*targetCopy = *target;
objectsToBeCreated = lappend(objectsToBeCreated, targetCopy);
}
ObjectAddress *object = NULL;
foreach_declared_ptr(object, objectsToBeCreated)
{
List *dependencyCommands = GetDependencyCreateDDLCommands(object);
List *dependencyCommands = GetDependencyCreateDDLCommands(dependency);
ddlCommands = list_concat(ddlCommands, dependencyCommands);
/* create a new list with objects that actually created commands */
/* create a new list with dependencies that actually created commands */
if (list_length(dependencyCommands) > 0)
{
objectsWithCommands = lappend(objectsWithCommands, object);
dependenciesWithCommands = lappend(dependenciesWithCommands, dependency);
}
}
if (list_length(ddlCommands) <= 0)
@ -190,73 +95,40 @@ EnsureRequiredObjectSetExistOnAllNodes(const ObjectAddress *target,
* either get it now, or get it in citus_add_node after this transaction finishes and
* the pg_dist_object record becomes visible.
*/
List *remoteNodeList = ActivePrimaryRemoteNodeList(RowShareLock);
List *workerNodeList = ActivePrimaryNonCoordinatorNodeList(RowShareLock);
/*
* Lock objects to be created explicitly to make sure same DDL command won't be sent
* Lock dependent objects explicitly to make sure same DDL command won't be sent
* multiple times from parallel sessions.
*
* Sort the objects that will be created on workers to not to have any deadlock
* Sort dependencies that will be created on workers to not to have any deadlock
* issue if different sessions are creating different objects.
*/
List *addressSortedDependencies = SortList(objectsWithCommands,
List *addressSortedDependencies = SortList(dependenciesWithCommands,
ObjectAddressComparator);
foreach_declared_ptr(object, addressSortedDependencies)
foreach_ptr(dependency, addressSortedDependencies)
{
LockDatabaseObject(object->classId, object->objectId,
object->objectSubId, ExclusiveLock);
LockDatabaseObject(dependency->classId, dependency->objectId,
dependency->objectSubId, ExclusiveLock);
}
/*
* We need to propagate objects via the current user's metadata connection if
* any of the objects that we're interested in are created in the current transaction.
* Our assumption is that if we rely on an object created in the current transaction,
* then the current user, most probably, has permissions to create the target object
* as well.
*
* Note that, user still may not be able to create the target due to no permissions
* for any of its dependencies. But this is ok since it should be rare.
*
* If we opted to use a separate superuser connection for the target, then we would
* have visibility issues since propagated dependencies would be invisible to
* the separate connection until we locally commit.
*/
List *createdObjectList = GetAllSupportedDependenciesForObject(target);
/* consider target as well if we're requested to create it too */
if (requiredObjectSet == REQUIRE_OBJECT_AND_DEPENDENCIES)
WorkerNode *workerNode = NULL;
foreach_ptr(workerNode, workerNodeList)
{
ObjectAddress *targetCopy = palloc(sizeof(ObjectAddress));
*targetCopy = *target;
const char *nodeName = workerNode->workerName;
uint32 nodePort = workerNode->workerPort;
createdObjectList = lappend(createdObjectList, targetCopy);
}
if (HasAnyObjectInPropagatedObjects(createdObjectList))
{
SendCommandListToRemoteNodesWithMetadata(ddlCommands);
}
else
{
WorkerNode *workerNode = NULL;
foreach_declared_ptr(workerNode, remoteNodeList)
{
const char *nodeName = workerNode->workerName;
uint32 nodePort = workerNode->workerPort;
SendCommandListToWorkerOutsideTransaction(nodeName, nodePort,
CitusExtensionOwnerName(),
ddlCommands);
}
SendCommandListToWorkerOutsideTransaction(nodeName, nodePort,
CitusExtensionOwnerName(),
ddlCommands);
}
/*
* We do this after creating the objects on remote nodes, we make sure
* that objects have been created on remote nodes before marking them
* We do this after creating the objects on the workers, we make sure
* that objects have been created on worker nodes before marking them
* distributed, so MarkObjectDistributed wouldn't fail.
*/
foreach_declared_ptr(object, objectsWithCommands)
foreach_ptr(dependency, dependenciesWithCommands)
{
/*
* pg_dist_object entries must be propagated with the super user, since
@ -266,7 +138,7 @@ EnsureRequiredObjectSetExistOnAllNodes(const ObjectAddress *target,
* Only dependent object's metadata should be propagated with super user.
* Metadata of the table itself must be propagated with the current user.
*/
MarkObjectDistributedViaSuperUser(object);
MarkObjectDistributedViaSuperUser(dependency);
}
}
@ -279,7 +151,7 @@ void
EnsureAllObjectDependenciesExistOnAllNodes(const List *targets)
{
ObjectAddress *target = NULL;
foreach_declared_ptr(target, targets)
foreach_ptr(target, targets)
{
EnsureDependenciesExistOnAllNodes(target);
}
@ -336,13 +208,19 @@ DeferErrorIfCircularDependencyExists(const ObjectAddress *objectAddress)
List *dependencies = GetAllDependenciesForObject(objectAddress);
ObjectAddress *dependency = NULL;
foreach_declared_ptr(dependency, dependencies)
foreach_ptr(dependency, dependencies)
{
if (dependency->classId == objectAddress->classId &&
dependency->objectId == objectAddress->objectId &&
dependency->objectSubId == objectAddress->objectSubId)
{
char *objectDescription = getObjectDescription(objectAddress, false);
char *objectDescription = NULL;
#if PG_VERSION_NUM >= PG_VERSION_14
objectDescription = getObjectDescription(objectAddress, false);
#else
objectDescription = getObjectDescription(objectAddress);
#endif
StringInfo detailInfo = makeStringInfo();
appendStringInfo(detailInfo, "\"%s\" circularly depends itself, resolve "
@ -424,7 +302,7 @@ GetDistributableDependenciesForObject(const ObjectAddress *target)
/* filter the ones that can be distributed */
ObjectAddress *dependency = NULL;
foreach_declared_ptr(dependency, dependencies)
foreach_ptr(dependency, dependencies)
{
/*
* TODO: maybe we can optimize the logic applied in below line. Actually we
@ -508,7 +386,7 @@ GetDependencyCreateDDLCommands(const ObjectAddress *dependency)
INCLUDE_IDENTITY,
creatingShellTableOnRemoteNode);
TableDDLCommand *tableDDLCommand = NULL;
foreach_declared_ptr(tableDDLCommand, tableDDLCommands)
foreach_ptr(tableDDLCommand, tableDDLCommands)
{
Assert(CitusIsA(tableDDLCommand, TableDDLCommand));
commandList = lappend(commandList, GetTableDDLCommand(
@ -565,29 +443,16 @@ GetDependencyCreateDDLCommands(const ObjectAddress *dependency)
case OCLASS_DATABASE:
{
/*
* For the database where Citus is installed, only propagate the ownership of the
* database, only when the feature is on.
*
* This is because this database must exist on all nodes already so we shouldn't
* need to "CREATE" it on other nodes. However, we still need to correctly reflect
* its owner on other nodes too.
*/
if (dependency->objectId == MyDatabaseId && EnableAlterDatabaseOwner)
List *databaseDDLCommands = NIL;
/* only propagate the ownership of the database when the feature is on */
if (EnableAlterDatabaseOwner)
{
return DatabaseOwnerDDLCommands(dependency);
List *ownerDDLCommands = DatabaseOwnerDDLCommands(dependency);
databaseDDLCommands = list_concat(databaseDDLCommands, ownerDDLCommands);
}
/*
* For the other databases, create the database on all nodes, only when the feature
* is on.
*/
if (dependency->objectId != MyDatabaseId && EnableCreateDatabasePropagation)
{
return GetDatabaseMetadataSyncCommands(dependency->objectId);
}
return NIL;
return databaseDDLCommands;
}
case OCLASS_PROC:
@ -664,9 +529,9 @@ GetDependencyCreateDDLCommands(const ObjectAddress *dependency)
*/
Assert(false);
ereport(ERROR, (errmsg("unsupported object %s for distribution by citus",
getObjectTypeDescription(dependency,
getObjectTypeDescription_compat(dependency,
/* missingOk: */ false)),
/* missingOk: */ false)),
errdetail(
"citus tries to recreate an unsupported object on its workers"),
errhint("please report a bug as this should not be happening")));
@ -683,7 +548,7 @@ GetAllDependencyCreateDDLCommands(const List *dependencies)
List *commands = NIL;
ObjectAddress *dependency = NULL;
foreach_declared_ptr(dependency, dependencies)
foreach_ptr(dependency, dependencies)
{
commands = list_concat(commands, GetDependencyCreateDDLCommands(dependency));
}
@ -831,7 +696,7 @@ bool
ShouldPropagateAnyObject(List *addresses)
{
ObjectAddress *address = NULL;
foreach_declared_ptr(address, addresses)
foreach_ptr(address, addresses)
{
if (ShouldPropagateObject(address))
{
@ -853,7 +718,7 @@ FilterObjectAddressListByPredicate(List *objectAddressList, AddressPredicate pre
List *result = NIL;
ObjectAddress *address = NULL;
foreach_declared_ptr(address, objectAddressList)
foreach_ptr(address, objectAddressList)
{
if (predicate(address))
{

View File

@ -12,13 +12,11 @@
#include "postgres.h"
#include "pg_version_constants.h"
#include "distributed/commands.h"
#include "distributed/commands/utility_hook.h"
#include "distributed/comment.h"
#include "distributed/deparser.h"
#include "distributed/pg_version_constants.h"
#include "distributed/version_compat.h"
#include "distributed/commands/utility_hook.h"
static DistributeObjectOps NoDistributeOps = {
.deparse = NULL,
@ -152,17 +150,6 @@ static DistributeObjectOps Any_AlterRole = {
.address = AlterRoleStmtObjectAddress,
.markDistributed = false,
};
static DistributeObjectOps Any_AlterRoleRename = {
.deparse = DeparseRenameRoleStmt,
.qualify = NULL,
.preprocess = PreprocessAlterRoleRenameStmt,
.postprocess = NULL,
.operationType = DIST_OPS_ALTER,
.address = RenameRoleStmtObjectAddress,
.markDistributed = false,
};
static DistributeObjectOps Any_AlterRoleSet = {
.deparse = DeparseAlterRoleSetStmt,
.qualify = QualifyAlterRoleSetStmt,
@ -276,17 +263,6 @@ static DistributeObjectOps Any_CreateRole = {
.address = CreateRoleStmtObjectAddress,
.markDistributed = true,
};
static DistributeObjectOps Any_ReassignOwned = {
.deparse = DeparseReassignOwnedStmt,
.qualify = NULL,
.preprocess = NULL,
.postprocess = PostprocessReassignOwnedStmt,
.operationType = DIST_OPS_ALTER,
.address = NULL,
.markDistributed = false,
};
static DistributeObjectOps Any_DropOwned = {
.deparse = DeparseDropOwnedStmt,
.qualify = NULL,
@ -305,17 +281,6 @@ static DistributeObjectOps Any_DropRole = {
.address = NULL,
.markDistributed = false,
};
static DistributeObjectOps Role_Comment = {
.deparse = DeparseCommentStmt,
.qualify = NULL,
.preprocess = PreprocessAlterDistributedObjectStmt,
.postprocess = NULL,
.objectType = OBJECT_DATABASE,
.operationType = DIST_OPS_ALTER,
.address = CommentObjectAddress,
.markDistributed = false,
};
static DistributeObjectOps Any_CreateForeignServer = {
.deparse = DeparseCreateForeignServerStmt,
.qualify = NULL,
@ -329,8 +294,8 @@ static DistributeObjectOps Any_CreateForeignServer = {
static DistributeObjectOps Any_CreateSchema = {
.deparse = DeparseCreateSchemaStmt,
.qualify = NULL,
.preprocess = NULL,
.postprocess = PostprocessCreateSchemaStmt,
.preprocess = PreprocessCreateSchemaStmt,
.postprocess = NULL,
.operationType = DIST_OPS_CREATE,
.address = CreateSchemaStmtObjectAddress,
.markDistributed = true,
@ -398,42 +363,6 @@ static DistributeObjectOps Any_Rename = {
.address = NULL,
.markDistributed = false,
};
static DistributeObjectOps Any_SecLabel = {
.deparse = NULL,
.qualify = NULL,
.preprocess = NULL,
.postprocess = PostprocessAnySecLabelStmt,
.operationType = DIST_OPS_ALTER,
.address = SecLabelStmtObjectAddress,
.markDistributed = false,
};
static DistributeObjectOps Role_SecLabel = {
.deparse = DeparseRoleSecLabelStmt,
.qualify = NULL,
.preprocess = NULL,
.postprocess = PostprocessRoleSecLabelStmt,
.operationType = DIST_OPS_ALTER,
.address = SecLabelStmtObjectAddress,
.markDistributed = false,
};
static DistributeObjectOps Table_SecLabel = {
.deparse = DeparseTableSecLabelStmt,
.qualify = NULL,
.preprocess = NULL,
.postprocess = PostprocessTableOrColumnSecLabelStmt,
.operationType = DIST_OPS_ALTER,
.address = SecLabelStmtObjectAddress,
.markDistributed = false,
};
static DistributeObjectOps Column_SecLabel = {
.deparse = DeparseColumnSecLabelStmt,
.qualify = NULL,
.preprocess = NULL,
.postprocess = PostprocessTableOrColumnSecLabelStmt,
.operationType = DIST_OPS_ALTER,
.address = SecLabelStmtObjectAddress,
.markDistributed = false,
};
static DistributeObjectOps Attribute_Rename = {
.deparse = DeparseRenameAttributeStmt,
.qualify = QualifyRenameAttributeStmt,
@ -503,95 +432,6 @@ static DistributeObjectOps Database_AlterOwner = {
.address = AlterDatabaseOwnerObjectAddress,
.markDistributed = false,
};
static DistributeObjectOps Database_Grant = {
.deparse = DeparseGrantOnDatabaseStmt,
.qualify = NULL,
.preprocess = PreprocessGrantOnDatabaseStmt,
.postprocess = NULL,
.objectType = OBJECT_DATABASE,
.operationType = DIST_OPS_ALTER,
.address = NULL,
.markDistributed = false,
};
static DistributeObjectOps Database_Alter = {
.deparse = DeparseAlterDatabaseStmt,
.qualify = NULL,
.preprocess = PreprocessAlterDatabaseStmt,
.postprocess = NULL,
.objectType = OBJECT_DATABASE,
.operationType = DIST_OPS_ALTER,
.address = NULL,
.markDistributed = false,
};
static DistributeObjectOps Database_Create = {
.deparse = DeparseCreateDatabaseStmt,
.qualify = NULL,
.preprocess = PreprocessCreateDatabaseStmt,
.postprocess = PostprocessCreateDatabaseStmt,
.objectType = OBJECT_DATABASE,
.operationType = DIST_OPS_CREATE,
.address = CreateDatabaseStmtObjectAddress,
.markDistributed = true,
};
static DistributeObjectOps Database_Drop = {
.deparse = DeparseDropDatabaseStmt,
.qualify = NULL,
.preprocess = PreprocessDropDatabaseStmt,
.postprocess = NULL,
.objectType = OBJECT_DATABASE,
.operationType = DIST_OPS_DROP,
.address = DropDatabaseStmtObjectAddress,
.markDistributed = false,
};
static DistributeObjectOps Database_RefreshColl = {
.deparse = DeparseAlterDatabaseRefreshCollStmt,
.qualify = NULL,
.preprocess = PreprocessAlterDatabaseRefreshCollStmt,
.postprocess = NULL,
.objectType = OBJECT_DATABASE,
.operationType = DIST_OPS_ALTER,
.address = NULL,
.markDistributed = false,
};
static DistributeObjectOps Database_Set = {
.deparse = DeparseAlterDatabaseSetStmt,
.qualify = NULL,
.preprocess = PreprocessAlterDatabaseSetStmt,
.postprocess = NULL,
.objectType = OBJECT_DATABASE,
.operationType = DIST_OPS_ALTER,
.address = NULL,
.markDistributed = false,
};
static DistributeObjectOps Database_Comment = {
.deparse = DeparseCommentStmt,
.qualify = NULL,
.preprocess = PreprocessAlterDistributedObjectStmt,
.postprocess = NULL,
.objectType = OBJECT_DATABASE,
.operationType = DIST_OPS_ALTER,
.address = CommentObjectAddress,
.markDistributed = false,
};
static DistributeObjectOps Database_Rename = {
.deparse = DeparseAlterDatabaseRenameStmt,
.qualify = NULL,
.preprocess = PreprocessAlterDatabaseRenameStmt,
.postprocess = PostprocessAlterDatabaseRenameStmt,
.objectType = OBJECT_DATABASE,
.operationType = DIST_OPS_ALTER,
.address = NULL,
.markDistributed = false,
};
static DistributeObjectOps Domain_Alter = {
.deparse = DeparseAlterDomainStmt,
.qualify = QualifyAlterDomainStmt,
@ -951,6 +791,7 @@ static DistributeObjectOps Sequence_AlterOwner = {
.address = AlterSequenceOwnerStmtObjectAddress,
.markDistributed = false,
};
#if (PG_VERSION_NUM >= PG_VERSION_15)
static DistributeObjectOps Sequence_AlterPersistence = {
.deparse = DeparseAlterSequencePersistenceStmt,
.qualify = QualifyAlterSequencePersistenceStmt,
@ -960,6 +801,7 @@ static DistributeObjectOps Sequence_AlterPersistence = {
.address = AlterSequencePersistenceStmtObjectAddress,
.markDistributed = false,
};
#endif
static DistributeObjectOps Sequence_Drop = {
.deparse = DeparseDropSequenceStmt,
.qualify = QualifyDropSequenceStmt,
@ -1018,18 +860,13 @@ static DistributeObjectOps TextSearchConfig_AlterOwner = {
.markDistributed = false,
};
static DistributeObjectOps TextSearchConfig_Comment = {
.deparse = DeparseCommentStmt,
/* TODO: When adding new comment types we should create an abstracted
* qualify function, just like we have an abstract deparse
* and adress function
*/
.deparse = DeparseTextSearchConfigurationCommentStmt,
.qualify = QualifyTextSearchConfigurationCommentStmt,
.preprocess = PreprocessAlterDistributedObjectStmt,
.postprocess = NULL,
.objectType = OBJECT_TSCONFIGURATION,
.operationType = DIST_OPS_ALTER,
.address = CommentObjectAddress,
.address = TextSearchConfigurationCommentObjectAddress,
.markDistributed = false,
};
static DistributeObjectOps TextSearchConfig_Define = {
@ -1092,13 +929,13 @@ static DistributeObjectOps TextSearchDict_AlterOwner = {
.markDistributed = false,
};
static DistributeObjectOps TextSearchDict_Comment = {
.deparse = DeparseCommentStmt,
.deparse = DeparseTextSearchDictionaryCommentStmt,
.qualify = QualifyTextSearchDictionaryCommentStmt,
.preprocess = PreprocessAlterDistributedObjectStmt,
.postprocess = NULL,
.objectType = OBJECT_TSDICTIONARY,
.operationType = DIST_OPS_ALTER,
.address = CommentObjectAddress,
.address = TextSearchDictCommentObjectAddress,
.markDistributed = false,
};
static DistributeObjectOps TextSearchDict_Define = {
@ -1187,15 +1024,6 @@ static DistributeObjectOps Routine_Rename = {
.address = RenameFunctionStmtObjectAddress,
.markDistributed = false,
};
static DistributeObjectOps Schema_AlterOwner = {
.deparse = DeparseAlterSchemaOwnerStmt,
.qualify = NULL,
.preprocess = PreprocessAlterDistributedObjectStmt,
.operationType = DIST_OPS_ALTER,
.postprocess = NULL,
.address = AlterSchemaOwnerStmtObjectAddress,
.markDistributed = false,
};
static DistributeObjectOps Schema_Drop = {
.deparse = DeparseDropSchemaStmt,
.qualify = NULL,
@ -1416,13 +1244,14 @@ static DistributeObjectOps View_Rename = {
static DistributeObjectOps Trigger_Rename = {
.deparse = NULL,
.qualify = NULL,
.preprocess = NULL,
.preprocess = PreprocessAlterTriggerRenameStmt,
.operationType = DIST_OPS_ALTER,
.postprocess = PostprocessAlterTriggerRenameStmt,
.address = NULL,
.markDistributed = false,
};
/*
* GetDistributeObjectOps looks up the DistributeObjectOps which handles the node.
*
@ -1433,32 +1262,6 @@ GetDistributeObjectOps(Node *node)
{
switch (nodeTag(node))
{
case T_AlterDatabaseStmt:
{
return &Database_Alter;
}
case T_CreatedbStmt:
{
return &Database_Create;
}
case T_DropdbStmt:
{
return &Database_Drop;
}
case T_AlterDatabaseRefreshCollStmt:
{
return &Database_RefreshColl;
}
case T_AlterDatabaseSetStmt:
{
return &Database_Set;
}
case T_AlterDomainStmt:
{
return &Domain_Alter;
@ -1654,11 +1457,6 @@ GetDistributeObjectOps(Node *node)
return &Routine_AlterOwner;
}
case OBJECT_SCHEMA:
{
return &Schema_AlterOwner;
}
case OBJECT_STATISTIC_EXT:
{
return &Statistics_AlterOwner;
@ -1719,7 +1517,7 @@ GetDistributeObjectOps(Node *node)
case T_AlterTableStmt:
{
AlterTableStmt *stmt = castNode(AlterTableStmt, node);
switch (stmt->objtype)
switch (AlterTableStmtObjType_compat(stmt))
{
case OBJECT_TYPE:
{
@ -1743,6 +1541,7 @@ GetDistributeObjectOps(Node *node)
case OBJECT_SEQUENCE:
{
#if (PG_VERSION_NUM >= PG_VERSION_15)
ListCell *cmdCell = NULL;
foreach(cmdCell, stmt->cmds)
{
@ -1770,6 +1569,7 @@ GetDistributeObjectOps(Node *node)
}
}
}
#endif
/*
* Prior to PG15, the only Alter Table statement
@ -1826,16 +1626,6 @@ GetDistributeObjectOps(Node *node)
return &TextSearchDict_Comment;
}
case OBJECT_DATABASE:
{
return &Database_Comment;
}
case OBJECT_ROLE:
{
return &Role_Comment;
}
default:
{
return &NoDistributeOps;
@ -1945,11 +1735,6 @@ GetDistributeObjectOps(Node *node)
return &Any_DropOwned;
}
case T_ReassignOwnedStmt:
{
return &Any_ReassignOwned;
}
case T_DropStmt:
{
DropStmt *stmt = castNode(DropStmt, node);
@ -2112,11 +1897,6 @@ GetDistributeObjectOps(Node *node)
return &Routine_Grant;
}
case OBJECT_DATABASE:
{
return &Database_Grant;
}
default:
{
return &Any_Grant;
@ -2144,31 +1924,6 @@ GetDistributeObjectOps(Node *node)
return &Vacuum_Analyze;
}
case T_SecLabelStmt:
{
SecLabelStmt *stmt = castNode(SecLabelStmt, node);
switch (stmt->objtype)
{
case OBJECT_ROLE:
{
return &Role_SecLabel;
}
case OBJECT_TABLE:
{
return &Table_SecLabel;
}
case OBJECT_COLUMN:
{
return &Column_SecLabel;
}
default:
return &Any_SecLabel;
}
}
case T_RenameStmt:
{
RenameStmt *stmt = castNode(RenameStmt, node);
@ -2189,11 +1944,6 @@ GetDistributeObjectOps(Node *node)
return &Collation_Rename;
}
case OBJECT_DATABASE:
{
return &Database_Rename;
}
case OBJECT_DOMAIN:
{
return &Domain_Rename;
@ -2224,11 +1974,6 @@ GetDistributeObjectOps(Node *node)
return &Publication_Rename;
}
case OBJECT_ROLE:
{
return &Any_AlterRoleRename;
}
case OBJECT_ROUTINE:
{
return &Routine_Rename;

View File

@ -64,8 +64,7 @@ CreateDomainStmt *
RecreateDomainStmt(Oid domainOid)
{
CreateDomainStmt *stmt = makeNode(CreateDomainStmt);
stmt->domainname = stringToQualifiedNameList_compat(format_type_be_qualified(
domainOid));
stmt->domainname = stringToQualifiedNameList(format_type_be_qualified(domainOid));
HeapTuple tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(domainOid));
if (!HeapTupleIsValid(tup))
@ -207,10 +206,14 @@ MakeCollateClauseFromOid(Oid collationOid)
List *objName = NIL;
List *objArgs = NIL;
#if PG_VERSION_NUM >= PG_VERSION_14
getObjectIdentityParts(&collateAddress, &objName, &objArgs, false);
#else
getObjectIdentityParts(&collateAddress, &objName, &objArgs);
#endif
char *name = NULL;
foreach_declared_ptr(name, objName)
foreach_ptr(name, objName)
{
collateClause->collname = lappend(collateClause->collname, makeString(name));
}

View File

@ -9,21 +9,19 @@
*/
#include "postgres.h"
#include "miscadmin.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "distributed/colocation_utils.h"
#include "distributed/commands.h"
#include "distributed/commands/utility_hook.h"
#include "distributed/commands.h"
#include "distributed/metadata_utility.h"
#include "distributed/coordinator_protocol.h"
#include "distributed/metadata_sync.h"
#include "distributed/metadata_utility.h"
#include "distributed/multi_partitioning_utils.h"
#include "distributed/tenant_schema_metadata.h"
#include "distributed/worker_transaction.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
/* local function forward declarations */
@ -92,27 +90,7 @@ master_remove_partition_metadata(PG_FUNCTION_ARGS)
DeletePartitionRow(relationId);
/*
* We want to keep using the same colocation group for the tenant even if
* all the tables that belong to it are dropped and new tables are created
* for the tenant etc. For this reason, if a colocation group belongs to a
* tenant schema, we don't delete the colocation group even if there are no
* tables that belong to it.
*
* We do the same if system catalog cannot find the schema of the table
* because this means that the whole schema is dropped.
*
* In that case, we want to delete the colocation group regardless of
* whether the schema is a tenant schema or not. Even more, calling
* IsTenantSchema() with InvalidOid would cause an error, hence we check
* whether the schema is valid beforehand.
*/
bool missingOk = true;
Oid schemaId = get_namespace_oid(schemaName, missingOk);
if (!OidIsValid(schemaId) || !IsTenantSchema(schemaId))
{
DeleteColocationGroupIfNoTablesBelong(colocationId);
}
DeleteColocationGroupIfNoTablesBelong(colocationId);
PG_RETURN_VOID();
}

View File

@ -12,35 +12,32 @@
#include "access/genam.h"
#include "access/xact.h"
#include "citus_version.h"
#include "catalog/dependency.h"
#include "catalog/pg_depend.h"
#include "catalog/pg_extension_d.h"
#include "columnar/columnar.h"
#include "catalog/pg_foreign_data_wrapper.h"
#include "commands/defrem.h"
#include "commands/extension.h"
#include "foreign/foreign.h"
#include "nodes/makefuncs.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
#include "citus_version.h"
#include "columnar/columnar.h"
#include "distributed/citus_ruleutils.h"
#include "distributed/commands.h"
#include "distributed/commands/utility_hook.h"
#include "distributed/coordinator_protocol.h"
#include "distributed/deparser.h"
#include "distributed/listutils.h"
#include "distributed/coordinator_protocol.h"
#include "distributed/metadata_sync.h"
#include "distributed/metadata/dependency.h"
#include "distributed/metadata/distobject.h"
#include "distributed/metadata_sync.h"
#include "distributed/multi_executor.h"
#include "distributed/relation_access_tracking.h"
#include "distributed/transaction_management.h"
#include "foreign/foreign.h"
#include "nodes/makefuncs.h"
#include "utils/lsyscache.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/syscache.h"
/* Local functions forward declarations for helper functions */
@ -53,7 +50,7 @@ static List * GetAllViews(void);
static bool ShouldPropagateExtensionCommand(Node *parseTree);
static bool IsAlterExtensionSetSchemaCitus(Node *parseTree);
static Node * RecreateExtensionStmt(Oid extensionOid);
static List * GenerateGrantCommandsOnExtensionDependentFDWs(Oid extensionId);
static List * GenerateGrantCommandsOnExtesionDependentFDWs(Oid extensionId);
/*
@ -274,7 +271,7 @@ PreprocessDropExtensionStmt(Node *node, const char *queryString,
/* unmark each distributed extension */
ObjectAddress *address = NULL;
foreach_declared_ptr(address, distributedExtensionAddresses)
foreach_ptr(address, distributedExtensionAddresses)
{
UnmarkObjectDistributed(address);
}
@ -313,7 +310,7 @@ FilterDistributedExtensions(List *extensionObjectList)
List *extensionNameList = NIL;
String *objectName = NULL;
foreach_declared_ptr(objectName, extensionObjectList)
foreach_ptr(objectName, extensionObjectList)
{
const char *extensionName = strVal(objectName);
const bool missingOk = true;
@ -351,7 +348,7 @@ ExtensionNameListToObjectAddressList(List *extensionObjectList)
List *extensionObjectAddressList = NIL;
String *objectName;
foreach_declared_ptr(objectName, extensionObjectList)
foreach_ptr(objectName, extensionObjectList)
{
/*
* We set missingOk to false as we assume all the objects in
@ -527,7 +524,7 @@ MarkExistingObjectDependenciesDistributedIfSupported()
List *citusTableIdList = CitusTableTypeIdList(ANY_CITUS_TABLE_TYPE);
Oid citusTableId = InvalidOid;
foreach_declared_oid(citusTableId, citusTableIdList)
foreach_oid(citusTableId, citusTableIdList)
{
if (!ShouldMarkRelationDistributed(citusTableId))
{
@ -571,7 +568,7 @@ MarkExistingObjectDependenciesDistributedIfSupported()
*/
List *viewList = GetAllViews();
Oid viewOid = InvalidOid;
foreach_declared_oid(viewOid, viewList)
foreach_oid(viewOid, viewList)
{
if (!ShouldMarkRelationDistributed(viewOid))
{
@ -605,7 +602,7 @@ MarkExistingObjectDependenciesDistributedIfSupported()
List *distributedObjectAddressList = GetDistributedObjectAddressList();
ObjectAddress *distributedObjectAddress = NULL;
foreach_declared_ptr(distributedObjectAddress, distributedObjectAddressList)
foreach_ptr(distributedObjectAddress, distributedObjectAddressList)
{
List *distributableDependencyObjectAddresses =
GetDistributableDependenciesForObject(distributedObjectAddress);
@ -627,7 +624,7 @@ MarkExistingObjectDependenciesDistributedIfSupported()
SetLocalEnableMetadataSync(false);
ObjectAddress *objectAddress = NULL;
foreach_declared_ptr(objectAddress, uniqueObjectAddresses)
foreach_ptr(objectAddress, uniqueObjectAddresses)
{
MarkObjectDistributed(objectAddress);
}
@ -776,7 +773,7 @@ PreprocessCreateExtensionStmtForCitusColumnar(Node *parsetree)
/*create extension citus version xxx*/
if (newVersionValue)
{
char *newVersion = pstrdup(defGetString(newVersionValue));
char *newVersion = strdup(defGetString(newVersionValue));
versionNumber = GetExtensionVersionNumber(newVersion);
}
@ -796,7 +793,7 @@ PreprocessCreateExtensionStmtForCitusColumnar(Node *parsetree)
Oid citusOid = get_extension_oid("citus", true);
if (citusOid != InvalidOid)
{
char *curCitusVersion = pstrdup(get_extension_version(citusOid));
char *curCitusVersion = strdup(get_extension_version(citusOid));
int curCitusVersionNum = GetExtensionVersionNumber(curCitusVersion);
if (curCitusVersionNum < 1110)
{
@ -831,7 +828,7 @@ IsDropCitusExtensionStmt(Node *parseTree)
/* now that we have a DropStmt, check if citus extension is among the objects to dropped */
String *objectName;
foreach_declared_ptr(objectName, dropStmt->objects)
foreach_ptr(objectName, dropStmt->objects)
{
const char *extensionName = strVal(objectName);
@ -891,7 +888,7 @@ PreprocessAlterExtensionCitusStmtForCitusColumnar(Node *parseTree)
if (newVersionValue)
{
char *newVersion = defGetString(newVersionValue);
double newVersionNumber = GetExtensionVersionNumber(pstrdup(newVersion));
double newVersionNumber = GetExtensionVersionNumber(strdup(newVersion));
/*alter extension citus update to version >= 11.1-1, and no citus_columnar installed */
if (newVersionNumber >= 1110 && citusColumnarOid == InvalidOid)
@ -935,7 +932,7 @@ PostprocessAlterExtensionCitusStmtForCitusColumnar(Node *parseTree)
if (newVersionValue)
{
char *newVersion = defGetString(newVersionValue);
double newVersionNumber = GetExtensionVersionNumber(pstrdup(newVersion));
double newVersionNumber = GetExtensionVersionNumber(strdup(newVersion));
if (newVersionNumber >= 1110 && citusColumnarOid != InvalidOid)
{
/*upgrade citus, after "ALTER EXTENSION citus update to xxx" updates citus_columnar Y to version Z. */
@ -988,7 +985,7 @@ CreateExtensionDDLCommand(const ObjectAddress *extensionAddress)
/* any privilege granted on FDWs that belong to the extension should be included */
List *FDWGrants =
GenerateGrantCommandsOnExtensionDependentFDWs(extensionAddress->objectId);
GenerateGrantCommandsOnExtesionDependentFDWs(extensionAddress->objectId);
ddlCommands = list_concat(ddlCommands, FDWGrants);
@ -1051,17 +1048,17 @@ RecreateExtensionStmt(Oid extensionOid)
/*
* GenerateGrantCommandsOnExtensionDependentFDWs returns a list of commands that GRANTs
* GenerateGrantCommandsOnExtesionDependentFDWs returns a list of commands that GRANTs
* the privileges on FDWs that are depending on the given extension.
*/
static List *
GenerateGrantCommandsOnExtensionDependentFDWs(Oid extensionId)
GenerateGrantCommandsOnExtesionDependentFDWs(Oid extensionId)
{
List *commands = NIL;
List *FDWOids = GetDependentFDWsToExtension(extensionId);
Oid FDWOid = InvalidOid;
foreach_declared_oid(FDWOid, FDWOids)
foreach_oid(FDWOid, FDWOids)
{
Acl *aclEntry = GetPrivilegesForFDW(FDWOid);
@ -1093,26 +1090,33 @@ List *
GetDependentFDWsToExtension(Oid extensionId)
{
List *extensionFDWs = NIL;
ScanKeyData key[1];
ScanKeyData key[3];
int scanKeyCount = 3;
HeapTuple tup;
Relation pgDepend = table_open(DependRelationId, AccessShareLock);
ScanKeyInit(&key[0],
Anum_pg_depend_refclassid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(ExtensionRelationId));
ScanKeyInit(&key[1],
Anum_pg_depend_refobjid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(extensionId));
ScanKeyInit(&key[2],
Anum_pg_depend_classid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(ForeignDataWrapperRelationId));
SysScanDesc scan = systable_beginscan(pgDepend, DependDependerIndexId, true,
NULL, lengthof(key), key);
SysScanDesc scan = systable_beginscan(pgDepend, InvalidOid, false,
NULL, scanKeyCount, key);
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
Form_pg_depend pgDependEntry = (Form_pg_depend) GETSTRUCT(tup);
if (pgDependEntry->deptype == DEPENDENCY_EXTENSION &&
pgDependEntry->refclassid == ExtensionRelationId &&
pgDependEntry->refobjid == extensionId)
if (pgDependEntry->deptype == DEPENDENCY_EXTENSION)
{
extensionFDWs = lappend_oid(extensionFDWs, pgDependEntry->objid);
}

View File

@ -12,16 +12,27 @@
#include "postgres.h"
#include "miscadmin.h"
#include "distributed/pg_version_constants.h"
#include "access/genam.h"
#include "access/htup_details.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "catalog/namespace.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_depend.h"
#include "access/genam.h"
#include "catalog/pg_type.h"
#include "distributed/colocation_utils.h"
#include "distributed/commands.h"
#include "distributed/commands/sequence.h"
#include "distributed/coordinator_protocol.h"
#include "distributed/listutils.h"
#include "distributed/coordinator_protocol.h"
#include "distributed/multi_join_order.h"
#include "distributed/namespace_utils.h"
#include "distributed/reference_table_utils.h"
#include "distributed/utils/array_type.h"
#include "distributed/version_compat.h"
#include "miscadmin.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
@ -31,20 +42,6 @@
#include "utils/ruleutils.h"
#include "utils/syscache.h"
#include "pg_version_constants.h"
#include "distributed/colocation_utils.h"
#include "distributed/commands.h"
#include "distributed/commands/sequence.h"
#include "distributed/coordinator_protocol.h"
#include "distributed/hash_helpers.h"
#include "distributed/listutils.h"
#include "distributed/multi_join_order.h"
#include "distributed/namespace_utils.h"
#include "distributed/reference_table_utils.h"
#include "distributed/utils/array_type.h"
#include "distributed/version_compat.h"
#define BehaviorIsRestrictOrNoAction(x) \
((x) == FKCONSTR_ACTION_NOACTION || (x) == FKCONSTR_ACTION_RESTRICT)
@ -135,7 +132,7 @@ EnsureNoFKeyFromTableType(Oid relationId, int tableTypeFlag)
/*
* EnsureNoFKeyToTableType ensures that given relation is not referencing any table specified
* EnsureNoFKeyToTableType ensures that given relation is not referencing by any table specified
* by table type flag.
*/
void
@ -202,7 +199,7 @@ ErrorIfUnsupportedForeignConstraintExists(Relation relation, char referencingDis
List *foreignKeyOids = GetForeignKeyOids(referencingTableId, flags);
Oid foreignKeyOid = InvalidOid;
foreach_declared_oid(foreignKeyOid, foreignKeyOids)
foreach_oid(foreignKeyOid, foreignKeyOids)
{
HeapTuple heapTuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(foreignKeyOid));
@ -306,11 +303,6 @@ ErrorIfUnsupportedForeignConstraintExists(Relation relation, char referencingDis
/*
* Foreign keys from citus local tables or reference tables to distributed
* tables are not supported.
*
* We could support foreign keys from references tables to single-shard
* tables but this doesn't seem useful a lot. However, if we decide supporting
* this, then we need to expand relation access tracking check for the single-shard
* tables too.
*/
if (referencingIsCitusLocalOrRefTable && !referencedIsCitusLocalOrRefTable)
{
@ -369,12 +361,7 @@ ErrorIfUnsupportedForeignConstraintExists(Relation relation, char referencingDis
* if tables are hash-distributed and colocated, we need to make sure that
* the distribution key is included in foreign constraint.
*/
bool referencedIsSingleShardTable =
IsSingleShardTableByDistParams(referencedDistMethod,
referencedReplicationModel,
referencedColocationId);
if (!referencedIsCitusLocalOrRefTable && !referencedIsSingleShardTable &&
!foreignConstraintOnDistKey)
if (!referencedIsCitusLocalOrRefTable && !foreignConstraintOnDistKey)
{
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot create foreign key constraint"),
@ -414,7 +401,7 @@ ForeignKeySetsNextValColumnToDefault(HeapTuple pgConstraintTuple)
List *setDefaultAttrs = ForeignKeyGetDefaultingAttrs(pgConstraintTuple);
AttrNumber setDefaultAttr = InvalidAttrNumber;
foreach_declared_int(setDefaultAttr, setDefaultAttrs)
foreach_int(setDefaultAttr, setDefaultAttrs)
{
if (ColumnDefaultsToNextVal(pgConstraintForm->conrelid, setDefaultAttr))
{
@ -467,6 +454,7 @@ ForeignKeyGetDefaultingAttrs(HeapTuple pgConstraintTuple)
}
List *onDeleteSetDefColumnList = NIL;
#if PG_VERSION_NUM >= PG_VERSION_15
Datum onDeleteSetDefColumnsDatum = SysCacheGetAttr(CONSTROID, pgConstraintTuple,
Anum_pg_constraint_confdelsetcols,
&isNull);
@ -481,6 +469,7 @@ ForeignKeyGetDefaultingAttrs(HeapTuple pgConstraintTuple)
onDeleteSetDefColumnList =
IntegerArrayTypeToList(DatumGetArrayTypeP(onDeleteSetDefColumnsDatum));
}
#endif
if (list_length(onDeleteSetDefColumnList) == 0)
{
@ -725,7 +714,7 @@ ColumnAppearsInForeignKeyToReferenceTable(char *columnName, Oid relationId)
GetForeignKeyIdsForColumn(columnName, relationId, searchForeignKeyColumnFlags);
Oid foreignKeyId = InvalidOid;
foreach_declared_oid(foreignKeyId, foreignKeyIdsColumnAppeared)
foreach_oid(foreignKeyId, foreignKeyIdsColumnAppeared)
{
Oid referencedTableId = GetReferencedTableId(foreignKeyId);
if (IsCitusTableType(referencedTableId, REFERENCE_TABLE))
@ -840,22 +829,6 @@ GetForeignConstraintToReferenceTablesCommands(Oid relationId)
}
/*
* GetForeignConstraintToReferenceTablesCommands takes in a relationId, and
* returns the list of foreign constraint commands needed to reconstruct
* foreign key constraints that the table is involved in as the "referenced"
* one and the "referencing" table is a reference table.
*/
List *
GetForeignConstraintFromOtherReferenceTablesCommands(Oid relationId)
{
int flags = INCLUDE_REFERENCED_CONSTRAINTS |
EXCLUDE_SELF_REFERENCES |
INCLUDE_REFERENCE_TABLES;
return GetForeignConstraintCommandsInternal(relationId, flags);
}
/*
* GetForeignConstraintToDistributedTablesCommands takes in a relationId, and
* returns the list of foreign constraint commands needed to reconstruct
@ -896,10 +869,10 @@ GetForeignConstraintCommandsInternal(Oid relationId, int flags)
List *foreignKeyCommands = NIL;
int saveNestLevel = PushEmptySearchPath();
PushOverrideEmptySearchPath(CurrentMemoryContext);
Oid foreignKeyOid = InvalidOid;
foreach_declared_oid(foreignKeyOid, foreignKeyOids)
foreach_oid(foreignKeyOid, foreignKeyOids)
{
char *statementDef = pg_get_constraintdef_command(foreignKeyOid);
@ -907,54 +880,12 @@ GetForeignConstraintCommandsInternal(Oid relationId, int flags)
}
/* revert back to original search_path */
PopEmptySearchPath(saveNestLevel);
PopOverrideSearchPath();
return foreignKeyCommands;
}
/*
* GetFKeyCreationCommandsRelationInvolvedWithTableType returns a list of DDL
* commands to recreate the foreign keys that relation with relationId is involved
* with given table type.
*/
List *
GetFKeyCreationCommandsRelationInvolvedWithTableType(Oid relationId, int tableTypeFlag)
{
int referencingFKeysFlag = INCLUDE_REFERENCING_CONSTRAINTS |
tableTypeFlag;
List *referencingFKeyCreationCommands =
GetForeignConstraintCommandsInternal(relationId, referencingFKeysFlag);
/* already captured self referencing foreign keys, so use EXCLUDE_SELF_REFERENCES */
int referencedFKeysFlag = INCLUDE_REFERENCED_CONSTRAINTS |
EXCLUDE_SELF_REFERENCES |
tableTypeFlag;
List *referencedFKeyCreationCommands =
GetForeignConstraintCommandsInternal(relationId, referencedFKeysFlag);
return list_concat(referencingFKeyCreationCommands, referencedFKeyCreationCommands);
}
/*
* DropFKeysRelationInvolvedWithTableType drops foreign keys that relation
* with relationId is involved with given table type.
*/
void
DropFKeysRelationInvolvedWithTableType(Oid relationId, int tableTypeFlag)
{
int referencingFKeysFlag = INCLUDE_REFERENCING_CONSTRAINTS |
tableTypeFlag;
DropRelationForeignKeys(relationId, referencingFKeysFlag);
/* already captured self referencing foreign keys, so use EXCLUDE_SELF_REFERENCES */
int referencedFKeysFlag = INCLUDE_REFERENCED_CONSTRAINTS |
EXCLUDE_SELF_REFERENCES |
tableTypeFlag;
DropRelationForeignKeys(relationId, referencedFKeysFlag);
}
/*
* HasForeignKeyWithLocalTable returns true if relation has foreign key
* relationship with a local table.
@ -1155,7 +1086,7 @@ static Oid
FindForeignKeyOidWithName(List *foreignKeyOids, const char *inputConstraintName)
{
Oid foreignKeyOid = InvalidOid;
foreach_declared_oid(foreignKeyOid, foreignKeyOids)
foreach_oid(foreignKeyOid, foreignKeyOids)
{
char *constraintName = get_constraint_name(foreignKeyOid);
@ -1198,114 +1129,6 @@ TableHasExternalForeignKeys(Oid relationId)
}
/*
* ForeignConstraintMatchesFlags is a function with logic that's very specific
* to GetForeignKeyOids. There's no reason to use it in any other context.
*/
static bool
ForeignConstraintMatchesFlags(Form_pg_constraint constraintForm,
int flags)
{
if (constraintForm->contype != CONSTRAINT_FOREIGN)
{
return false;
}
bool inheritedConstraint = OidIsValid(constraintForm->conparentid);
if (inheritedConstraint)
{
/*
* We only consider the constraints that are explicitly created on
* the table as we already process the constraints from parent tables
* implicitly when a command is issued
*/
return false;
}
bool excludeSelfReference = (flags & EXCLUDE_SELF_REFERENCES);
bool isSelfReference = (constraintForm->conrelid == constraintForm->confrelid);
if (excludeSelfReference && isSelfReference)
{
return false;
}
Oid otherTableId = InvalidOid;
if (flags & INCLUDE_REFERENCING_CONSTRAINTS)
{
otherTableId = constraintForm->confrelid;
}
else
{
otherTableId = constraintForm->conrelid;
}
return IsTableTypeIncluded(otherTableId, flags);
}
/*
* GetForeignKeyOidsForReferencedTable returns a list of foreign key OIDs that
* reference the relationId and match the given flags.
*
* This is separated from GetForeignKeyOids because we need to scan pg_depend
* instead of pg_constraint directly. The reason for this is that there is no
* index on the confrelid of pg_constraint, so searching by that column
* requires a seqscan.
*/
static List *
GetForeignKeyOidsForReferencedTable(Oid relationId, int flags)
{
HTAB *foreignKeyOidsSet = CreateSimpleHashSetWithName(
Oid, "ReferencingForeignKeyOidsSet");
List *foreignKeyOidsList = NIL;
ScanKeyData key[2];
HeapTuple dependTup;
Relation depRel = table_open(DependRelationId, AccessShareLock);
ScanKeyInit(&key[0],
Anum_pg_depend_refclassid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(RelationRelationId));
ScanKeyInit(&key[1],
Anum_pg_depend_refobjid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(relationId));
SysScanDesc scan = systable_beginscan(depRel, DependReferenceIndexId, true,
NULL, lengthof(key), key);
while (HeapTupleIsValid(dependTup = systable_getnext(scan)))
{
Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(dependTup);
if (deprec->classid != ConstraintRelationId ||
deprec->deptype != DEPENDENCY_NORMAL ||
hash_search(foreignKeyOidsSet, &deprec->objid, HASH_FIND, NULL))
{
continue;
}
HeapTuple constraintTup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(
deprec->objid));
if (!HeapTupleIsValid(constraintTup)) /* can happen during DROP TABLE */
{
continue;
}
Form_pg_constraint constraint = (Form_pg_constraint) GETSTRUCT(constraintTup);
if (constraint->confrelid == relationId &&
ForeignConstraintMatchesFlags(constraint, flags))
{
foreignKeyOidsList = lappend_oid(foreignKeyOidsList, constraint->oid);
hash_search(foreignKeyOidsSet, &constraint->oid, HASH_ENTER, NULL);
}
ReleaseSysCache(constraintTup);
}
systable_endscan(scan);
table_close(depRel, AccessShareLock);
return foreignKeyOidsList;
}
/*
* GetForeignKeyOids takes in a relationId, and returns a list of OIDs for
* foreign constraints that the relation with relationId is involved according
@ -1315,8 +1138,9 @@ GetForeignKeyOidsForReferencedTable(Oid relationId, int flags)
List *
GetForeignKeyOids(Oid relationId, int flags)
{
bool extractReferencing PG_USED_FOR_ASSERTS_ONLY = (flags &
INCLUDE_REFERENCING_CONSTRAINTS);
AttrNumber pgConstraintTargetAttrNumber = InvalidAttrNumber;
bool extractReferencing = (flags & INCLUDE_REFERENCING_CONSTRAINTS);
bool extractReferenced = (flags & INCLUDE_REFERENCED_CONSTRAINTS);
/*
@ -1327,10 +1151,22 @@ GetForeignKeyOids(Oid relationId, int flags)
Assert(!(extractReferencing && extractReferenced));
Assert(extractReferencing || extractReferenced);
if (extractReferenced)
bool useIndex = false;
Oid indexOid = InvalidOid;
if (extractReferencing)
{
return GetForeignKeyOidsForReferencedTable(relationId, flags);
pgConstraintTargetAttrNumber = Anum_pg_constraint_conrelid;
useIndex = true;
indexOid = ConstraintRelidTypidNameIndexId;
}
else if (extractReferenced)
{
pgConstraintTargetAttrNumber = Anum_pg_constraint_confrelid;
}
bool excludeSelfReference = (flags & EXCLUDE_SELF_REFERENCES);
List *foreignKeyOids = NIL;
@ -1338,22 +1174,62 @@ GetForeignKeyOids(Oid relationId, int flags)
int scanKeyCount = 1;
Relation pgConstraint = table_open(ConstraintRelationId, AccessShareLock);
ScanKeyInit(&scanKey[0], Anum_pg_constraint_conrelid,
BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relationId));
SysScanDesc scanDescriptor = systable_beginscan(pgConstraint,
ConstraintRelidTypidNameIndexId, true,
ScanKeyInit(&scanKey[0], pgConstraintTargetAttrNumber,
BTEqualStrategyNumber, F_OIDEQ, relationId);
SysScanDesc scanDescriptor = systable_beginscan(pgConstraint, indexOid, useIndex,
NULL, scanKeyCount, scanKey);
HeapTuple heapTuple;
while (HeapTupleIsValid(heapTuple = systable_getnext(scanDescriptor)))
HeapTuple heapTuple = systable_getnext(scanDescriptor);
while (HeapTupleIsValid(heapTuple))
{
Form_pg_constraint constraintForm = (Form_pg_constraint) GETSTRUCT(heapTuple);
if (ForeignConstraintMatchesFlags(constraintForm, flags))
if (constraintForm->contype != CONSTRAINT_FOREIGN)
{
foreignKeyOids = lappend_oid(foreignKeyOids, constraintForm->oid);
heapTuple = systable_getnext(scanDescriptor);
continue;
}
bool inheritedConstraint = OidIsValid(constraintForm->conparentid);
if (inheritedConstraint)
{
/*
* We only consider the constraints that are explicitly created on
* the table as we already process the constraints from parent tables
* implicitly when a command is issued
*/
heapTuple = systable_getnext(scanDescriptor);
continue;
}
Oid constraintId = constraintForm->oid;
bool isSelfReference = (constraintForm->conrelid == constraintForm->confrelid);
if (excludeSelfReference && isSelfReference)
{
heapTuple = systable_getnext(scanDescriptor);
continue;
}
Oid otherTableId = InvalidOid;
if (extractReferencing)
{
otherTableId = constraintForm->confrelid;
}
else if (extractReferenced)
{
otherTableId = constraintForm->conrelid;
}
if (!IsTableTypeIncluded(otherTableId, flags))
{
heapTuple = systable_getnext(scanDescriptor);
continue;
}
foreignKeyOids = lappend_oid(foreignKeyOids, constraintId);
heapTuple = systable_getnext(scanDescriptor);
}
systable_endscan(scanDescriptor);
@ -1428,10 +1304,6 @@ IsTableTypeIncluded(Oid relationId, int flags)
{
return (flags & INCLUDE_LOCAL_TABLES) != 0;
}
else if (IsCitusTableType(relationId, SINGLE_SHARD_DISTRIBUTED))
{
return (flags & INCLUDE_SINGLE_SHARD_TABLES) != 0;
}
else if (IsCitusTableType(relationId, DISTRIBUTED_TABLE))
{
return (flags & INCLUDE_DISTRIBUTED_TABLES) != 0;
@ -1470,7 +1342,7 @@ RelationInvolvedInAnyNonInheritedForeignKeys(Oid relationId)
List *foreignKeysRelationInvolved = list_concat(referencingForeignKeys,
referencedForeignKeys);
Oid foreignKeyId = InvalidOid;
foreach_declared_oid(foreignKeyId, foreignKeysRelationInvolved)
foreach_oid(foreignKeyId, foreignKeysRelationInvolved)
{
HeapTuple heapTuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(foreignKeyId));
if (!HeapTupleIsValid(heapTuple))

View File

@ -11,18 +11,17 @@
#include "postgres.h"
#include "catalog/pg_foreign_data_wrapper.h"
#include "distributed/commands/utility_hook.h"
#include "distributed/commands.h"
#include "distributed/deparser.h"
#include "distributed/listutils.h"
#include "distributed/metadata_sync.h"
#include "distributed/metadata/distobject.h"
#include "foreign/foreign.h"
#include "nodes/makefuncs.h"
#include "nodes/parsenodes.h"
#include "utils/syscache.h"
#include "distributed/commands.h"
#include "distributed/commands/utility_hook.h"
#include "distributed/deparser.h"
#include "distributed/listutils.h"
#include "distributed/metadata/distobject.h"
#include "distributed/metadata_sync.h"
static bool NameListHasFDWOwnedByDistributedExtension(List *FDWNames);
static ObjectAddress GetObjectAddressByFDWName(char *FDWName, bool missing_ok);
@ -86,7 +85,7 @@ static bool
NameListHasFDWOwnedByDistributedExtension(List *FDWNames)
{
String *FDWValue = NULL;
foreach_declared_ptr(FDWValue, FDWNames)
foreach_ptr(FDWValue, FDWNames)
{
/* captures the extension address during lookup */
ObjectAddress *extensionAddress = palloc0(sizeof(ObjectAddress));

View File

@ -9,18 +9,11 @@
*/
#include "postgres.h"
#include "miscadmin.h"
#include "catalog/pg_foreign_server.h"
#include "foreign/foreign.h"
#include "nodes/makefuncs.h"
#include "nodes/parsenodes.h"
#include "nodes/primnodes.h"
#include "utils/builtins.h"
#include "distributed/commands.h"
#include "distributed/commands/utility_hook.h"
#include "distributed/commands.h"
#include "distributed/deparser.h"
#include "distributed/listutils.h"
#include "distributed/log_utils.h"
@ -28,6 +21,11 @@
#include "distributed/metadata_sync.h"
#include "distributed/multi_executor.h"
#include "distributed/worker_transaction.h"
#include "foreign/foreign.h"
#include "nodes/makefuncs.h"
#include "nodes/parsenodes.h"
#include "nodes/primnodes.h"
#include "utils/builtins.h"
static char * GetForeignServerAlterOwnerCommand(Oid serverId);
static Node * RecreateForeignServerStmt(Oid serverId);
@ -229,7 +227,7 @@ RecreateForeignServerStmt(Oid serverId)
int location = -1;
DefElem *option = NULL;
foreach_declared_ptr(option, server->options)
foreach_ptr(option, server->options)
{
DefElem *copyOption = makeDefElem(option->defname, option->arg, location);
createStmt->options = lappend(createStmt->options, copyOption);
@ -247,7 +245,7 @@ static bool
NameListHasDistributedServer(List *serverNames)
{
String *serverValue = NULL;
foreach_declared_ptr(serverValue, serverNames)
foreach_ptr(serverValue, serverNames)
{
List *addresses = GetObjectAddressByServerName(strVal(serverValue), false);

View File

@ -18,47 +18,35 @@
*/
#include "postgres.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "funcapi.h"
#include "distributed/pg_version_constants.h"
#include "access/genam.h"
#include "access/htup_details.h"
#include "access/xact.h"
#include "catalog/pg_aggregate.h"
#include "catalog/dependency.h"
#include "catalog/namespace.h"
#include "catalog/pg_aggregate.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/extension.h"
#include "nodes/makefuncs.h"
#include "parser/parse_coerce.h"
#include "parser/parse_type.h"
#include "storage/lmgr.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/fmgrprotos.h"
#include "utils/lsyscache.h"
#include "utils/regproc.h"
#include "utils/syscache.h"
#include "pg_version_constants.h"
#include "distributed/citus_depended_object.h"
#include "distributed/citus_ruleutils.h"
#include "distributed/citus_safe_lib.h"
#include "distributed/colocation_utils.h"
#include "distributed/commands.h"
#include "distributed/commands/utility_hook.h"
#include "distributed/coordinator_protocol.h"
#include "distributed/deparser.h"
#include "distributed/listutils.h"
#include "distributed/maintenanced.h"
#include "distributed/metadata_utility.h"
#include "distributed/metadata/dependency.h"
#include "distributed/coordinator_protocol.h"
#include "distributed/metadata/distobject.h"
#include "distributed/metadata/pg_dist_object.h"
#include "distributed/metadata_sync.h"
#include "distributed/metadata_utility.h"
#include "distributed/multi_executor.h"
#include "distributed/namespace_utils.h"
#include "distributed/pg_dist_node.h"
@ -67,6 +55,16 @@
#include "distributed/version_compat.h"
#include "distributed/worker_create_or_replace.h"
#include "distributed/worker_transaction.h"
#include "nodes/makefuncs.h"
#include "parser/parse_coerce.h"
#include "parser/parse_type.h"
#include "storage/lmgr.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/fmgrprotos.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
#include "utils/regproc.h"
#define DISABLE_LOCAL_CHECK_FUNCTION_BODIES "SET LOCAL check_function_bodies TO off;"
#define RESET_CHECK_FUNCTION_BODIES "RESET check_function_bodies;"
@ -107,9 +105,6 @@ static void DistributeFunctionColocatedWithDistributedTable(RegProcedure funcOid
char *colocateWithTableName,
const ObjectAddress *
functionAddress);
static void DistributeFunctionColocatedWithSingleShardTable(const
ObjectAddress *functionAddress,
text *colocateWithText);
static void DistributeFunctionColocatedWithReferenceTable(const
ObjectAddress *functionAddress);
static List * FilterDistributedFunctions(GrantStmt *grantStmt);
@ -138,7 +133,6 @@ create_distributed_function(PG_FUNCTION_ARGS)
Oid distributionArgumentOid = InvalidOid;
bool colocatedWithReferenceTable = false;
bool colocatedWithSingleShardTable = false;
char *distributionArgumentName = NULL;
char *colocateWithTableName = NULL;
@ -193,8 +187,6 @@ create_distributed_function(PG_FUNCTION_ARGS)
Oid colocationRelationId = ResolveRelationId(colocateWithText, false);
colocatedWithReferenceTable = IsCitusTableType(colocationRelationId,
REFERENCE_TABLE);
colocatedWithSingleShardTable = IsCitusTableType(colocationRelationId,
SINGLE_SHARD_DISTRIBUTED);
}
}
@ -256,7 +248,7 @@ create_distributed_function(PG_FUNCTION_ARGS)
createFunctionSQL, alterFunctionOwnerSQL);
List *grantDDLCommands = GrantOnFunctionDDLCommands(funcOid);
char *grantOnFunctionSQL = NULL;
foreach_declared_ptr(grantOnFunctionSQL, grantDDLCommands)
foreach_ptr(grantOnFunctionSQL, grantDDLCommands)
{
appendStringInfo(&ddlCommand, ";%s", grantOnFunctionSQL);
}
@ -284,16 +276,11 @@ create_distributed_function(PG_FUNCTION_ARGS)
forceDelegationAddress,
functionAddress);
}
else if (!colocatedWithReferenceTable && !colocatedWithSingleShardTable)
else if (!colocatedWithReferenceTable)
{
DistributeFunctionColocatedWithDistributedTable(funcOid, colocateWithTableName,
functionAddress);
}
else if (colocatedWithSingleShardTable)
{
DistributeFunctionColocatedWithSingleShardTable(functionAddress,
colocateWithText);
}
else if (colocatedWithReferenceTable)
{
/*
@ -370,7 +357,7 @@ ErrorIfAnyNodeDoesNotHaveMetadata(void)
ActivePrimaryNonCoordinatorNodeList(ShareLock);
WorkerNode *workerNode = NULL;
foreach_declared_ptr(workerNode, workerNodeList)
foreach_ptr(workerNode, workerNodeList)
{
if (!workerNode->hasMetadata)
{
@ -448,25 +435,6 @@ DistributeFunctionColocatedWithDistributedTable(RegProcedure funcOid,
}
/*
* DistributeFunctionColocatedWithSingleShardTable updates pg_dist_object records for
* a function/procedure that is colocated with a single shard table.
*/
static void
DistributeFunctionColocatedWithSingleShardTable(const ObjectAddress *functionAddress,
text *colocateWithText)
{
/* get the single shard table's colocation id */
int colocationId = TableColocationId(ResolveRelationId(colocateWithText, false));
/* set distribution argument to NULL */
int *distributionArgumentIndex = NULL;
UpdateFunctionDistributionInfo(functionAddress, distributionArgumentIndex,
&colocationId,
NULL);
}
/*
* DistributeFunctionColocatedWithReferenceTable updates pg_dist_object records for
* a function/procedure that is colocated with a reference table.
@ -673,19 +641,6 @@ EnsureFunctionCanBeColocatedWithTable(Oid functionOid, Oid distributionColumnTyp
CitusTableCacheEntry *sourceTableEntry = GetCitusTableCacheEntry(sourceRelationId);
char sourceReplicationModel = sourceTableEntry->replicationModel;
if (IsCitusTableTypeCacheEntry(sourceTableEntry, SINGLE_SHARD_DISTRIBUTED) &&
distributionColumnType != InvalidOid)
{
char *functionName = get_func_name(functionOid);
char *sourceRelationName = get_rel_name(sourceRelationId);
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot colocate function \"%s\" and table \"%s\" because "
"distribution arguments are not supported when "
"colocating with single shard distributed tables.",
functionName, sourceRelationName)));
}
if (!IsCitusTableTypeCacheEntry(sourceTableEntry, HASH_DISTRIBUTED) &&
!IsCitusTableTypeCacheEntry(sourceTableEntry, REFERENCE_TABLE))
{
@ -782,7 +737,7 @@ UpdateFunctionDistributionInfo(const ObjectAddress *distAddress,
ScanKeyInit(&scanKey[1], Anum_pg_dist_object_objid, BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(distAddress->objectId));
ScanKeyInit(&scanKey[2], Anum_pg_dist_object_objsubid, BTEqualStrategyNumber,
F_INT4EQ, Int32GetDatum(distAddress->objectSubId));
F_INT4EQ, ObjectIdGetDatum(distAddress->objectSubId));
SysScanDesc scanDescriptor = systable_beginscan(pgDistObjectRel,
DistObjectPrimaryKeyIndexId(),
@ -885,7 +840,6 @@ UpdateFunctionDistributionInfo(const ObjectAddress *distAddress,
char *workerPgDistObjectUpdateCommand =
MarkObjectsDistributedCreateCommand(objectAddressList,
NIL,
distArgumentIndexList,
colocationIdList,
forceDelegationList);
@ -912,14 +866,15 @@ GetFunctionDDLCommand(const RegProcedure funcOid, bool useCreateOrReplace)
else
{
Datum sqlTextDatum = (Datum) 0;
int saveNestLevel = PushEmptySearchPath();
PushOverrideEmptySearchPath(CurrentMemoryContext);
sqlTextDatum = DirectFunctionCall1(pg_get_functiondef,
ObjectIdGetDatum(funcOid));
createFunctionSQL = TextDatumGetCString(sqlTextDatum);
/* revert back to original search_path */
PopEmptySearchPath(saveNestLevel);
PopOverrideSearchPath();
}
return createFunctionSQL;
@ -981,6 +936,7 @@ GetAggregateDDLCommand(const RegProcedure funcOid, bool useCreateOrReplace)
char *argmodes = NULL;
int insertorderbyat = -1;
int argsprinted = 0;
int inputargno = 0;
HeapTuple proctup = SearchSysCache1(PROCOID, funcOid);
if (!HeapTupleIsValid(proctup))
@ -1060,6 +1016,7 @@ GetAggregateDDLCommand(const RegProcedure funcOid, bool useCreateOrReplace)
}
}
inputargno++; /* this is a 1-based counter */
if (argsprinted == insertorderbyat)
{
appendStringInfoString(&buf, " ORDER BY ");
@ -1476,7 +1433,7 @@ CreateFunctionStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)
objectWithArgs->objname = stmt->funcname;
FunctionParameter *funcParam = NULL;
foreach_declared_ptr(funcParam, stmt->parameters)
foreach_ptr(funcParam, stmt->parameters)
{
if (ShouldAddFunctionSignature(funcParam->mode))
{
@ -1519,7 +1476,7 @@ DefineAggregateStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess
if (stmt->args != NIL)
{
FunctionParameter *funcParam = NULL;
foreach_declared_ptr(funcParam, linitial(stmt->args))
foreach_ptr(funcParam, linitial(stmt->args))
{
objectWithArgs->objargs = lappend(objectWithArgs->objargs,
funcParam->argType);
@ -1528,7 +1485,7 @@ DefineAggregateStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess
else
{
DefElem *defItem = NULL;
foreach_declared_ptr(defItem, stmt->definition)
foreach_ptr(defItem, stmt->definition)
{
/*
* If no explicit args are given, pg includes basetype in the signature.
@ -1641,7 +1598,7 @@ PreprocessAlterFunctionDependsStmt(Node *node, const char *queryString,
* workers
*/
const char *functionName =
getObjectIdentity(address, /* missingOk: */ false);
getObjectIdentity_compat(address, /* missingOk: */ false);
ereport(ERROR, (errmsg("distrtibuted functions are not allowed to depend on an "
"extension"),
errdetail("Function \"%s\" is already distributed. Functions from "
@ -1811,8 +1768,8 @@ GenerateBackupNameForProcCollision(const ObjectAddress *address)
List *newProcName = list_make2(namespace, makeString(newName));
/* don't need to rename if the input arguments don't match */
FuncCandidateList clist = FuncnameGetCandidates(newProcName, numargs, NIL,
false, false, false, true);
FuncCandidateList clist = FuncnameGetCandidates_compat(newProcName, numargs, NIL,
false, false, false, true);
for (; clist; clist = clist->next)
{
if (memcmp(clist->args, argtypes, sizeof(Oid) * numargs) == 0)
@ -1933,7 +1890,7 @@ static void
ErrorIfUnsupportedAlterFunctionStmt(AlterFunctionStmt *stmt)
{
DefElem *action = NULL;
foreach_declared_ptr(action, stmt->actions)
foreach_ptr(action, stmt->actions)
{
if (strcmp(action->defname, "set") == 0)
{
@ -2040,7 +1997,7 @@ PreprocessGrantOnFunctionStmt(Node *node, const char *queryString,
List *grantFunctionList = NIL;
ObjectAddress *functionAddress = NULL;
foreach_declared_ptr(functionAddress, distributedFunctions)
foreach_ptr(functionAddress, distributedFunctions)
{
ObjectWithArgs *distFunction = ObjectWithArgsFromOid(
functionAddress->objectId);
@ -2083,7 +2040,7 @@ PostprocessGrantOnFunctionStmt(Node *node, const char *queryString)
}
ObjectAddress *functionAddress = NULL;
foreach_declared_ptr(functionAddress, distributedFunctions)
foreach_ptr(functionAddress, distributedFunctions)
{
EnsureAllObjectDependenciesExistOnAllNodes(list_make1(functionAddress));
}
@ -2120,7 +2077,7 @@ FilterDistributedFunctions(GrantStmt *grantStmt)
/* iterate over all namespace names provided to get their oid's */
String *namespaceValue = NULL;
foreach_declared_ptr(namespaceValue, grantStmt->objects)
foreach_ptr(namespaceValue, grantStmt->objects)
{
char *nspname = strVal(namespaceValue);
bool missing_ok = false;
@ -2132,7 +2089,7 @@ FilterDistributedFunctions(GrantStmt *grantStmt)
* iterate over all distributed functions to filter the ones
* that belong to one of the namespaces from above
*/
foreach_declared_ptr(distributedFunction, distributedFunctionList)
foreach_ptr(distributedFunction, distributedFunctionList)
{
Oid namespaceOid = get_func_namespace(distributedFunction->objectId);
@ -2151,7 +2108,7 @@ FilterDistributedFunctions(GrantStmt *grantStmt)
{
bool missingOk = false;
ObjectWithArgs *objectWithArgs = NULL;
foreach_declared_ptr(objectWithArgs, grantStmt->objects)
foreach_ptr(objectWithArgs, grantStmt->objects)
{
ObjectAddress *functionAddress = palloc0(sizeof(ObjectAddress));
functionAddress->classId = ProcedureRelationId;

View File

@ -10,17 +10,15 @@
#include "postgres.h"
#include "lib/stringinfo.h"
#include "nodes/parsenodes.h"
#include "utils/lsyscache.h"
#include "distributed/citus_ruleutils.h"
#include "distributed/commands.h"
#include "distributed/commands/utility_hook.h"
#include "distributed/deparser.h"
#include "distributed/metadata/distobject.h"
#include "distributed/metadata_cache.h"
#include "distributed/version_compat.h"
#include "lib/stringinfo.h"
#include "nodes/parsenodes.h"
#include "utils/lsyscache.h"
/* Local functions forward declarations for helper functions */
@ -33,6 +31,7 @@ static List * CollectGrantTableIdList(GrantStmt *grantStmt);
* needed during the worker node portion of DDL execution before returning the
* DDLJobs in a List. If no distributed table is involved, this returns NIL.
*
* NB: So far column level privileges are not supported.
*/
List *
PreprocessGrantStmt(Node *node, const char *queryString,
@ -70,12 +69,9 @@ PreprocessGrantStmt(Node *node, const char *queryString,
return NIL;
}
EnsureCoordinator();
/* deparse the privileges */
if (grantStmt->privileges == NIL)
{
/* this is used for table level only */
appendStringInfo(&privsString, "ALL");
}
else
@ -91,44 +87,18 @@ PreprocessGrantStmt(Node *node, const char *queryString,
{
appendStringInfoString(&privsString, ", ");
}
if (priv->priv_name)
{
appendStringInfo(&privsString, "%s", priv->priv_name);
}
/*
* ALL can only be set alone.
* And ALL is not added as a keyword in priv_name by parser, but
* because there are column(s) defined, a grantStmt->privileges is
* defined. So we need to handle this special case here (see if
* condition above).
*/
else if (isFirst)
{
/* this is used for column level only */
appendStringInfo(&privsString, "ALL");
}
/*
* Instead of relying only on the syntax check done by Postgres and
* adding an assert here, add a default ERROR if ALL is not first
* and no priv_name is defined.
*/
else
{
ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR),
errmsg("Cannot parse GRANT/REVOKE privileges")));
}
isFirst = false;
if (priv->cols != NIL)
{
StringInfoData colsString;
initStringInfo(&colsString);
AppendColumnNameList(&colsString, priv->cols);
appendStringInfo(&privsString, "%s", colsString.data);
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("grant/revoke on column list is currently "
"unsupported")));
}
Assert(priv->priv_name != NULL);
appendStringInfo(&privsString, "%s", priv->priv_name);
}
}
@ -182,15 +152,6 @@ PreprocessGrantStmt(Node *node, const char *queryString,
appendStringInfo(&ddlString, "REVOKE %s%s ON %s FROM %s",
grantOption, privsString.data, targetString.data,
granteesString.data);
if (grantStmt->behavior == DROP_CASCADE)
{
appendStringInfoString(&ddlString, " CASCADE");
}
else
{
appendStringInfoString(&ddlString, " RESTRICT");
}
}
DDLJob *ddlJob = palloc0(sizeof(DDLJob));

View File

@ -10,8 +10,7 @@
#include "postgres.h"
#include "miscadmin.h"
#include "distributed/pg_version_constants.h"
#include "access/genam.h"
#include "access/htup_details.h"
#include "access/xact.h"
@ -21,7 +20,27 @@
#include "catalog/pg_class.h"
#include "commands/defrem.h"
#include "commands/tablecmds.h"
#include "distributed/citus_ruleutils.h"
#include "distributed/commands.h"
#include "distributed/commands/utility_hook.h"
#include "distributed/deparse_shard_query.h"
#include "distributed/deparser.h"
#include "distributed/distributed_planner.h"
#include "distributed/listutils.h"
#include "distributed/local_executor.h"
#include "distributed/coordinator_protocol.h"
#include "distributed/metadata_cache.h"
#include "distributed/multi_executor.h"
#include "distributed/multi_physical_planner.h"
#include "distributed/multi_partitioning_utils.h"
#include "distributed/namespace_utils.h"
#include "distributed/resource_lock.h"
#include "distributed/relation_access_tracking.h"
#include "distributed/relation_utils.h"
#include "distributed/version_compat.h"
#include "distributed/worker_manager.h"
#include "lib/stringinfo.h"
#include "miscadmin.h"
#include "nodes/parsenodes.h"
#include "parser/parse_utilcmd.h"
#include "storage/lmgr.h"
@ -31,32 +50,6 @@
#include "utils/lsyscache.h"
#include "utils/syscache.h"
#include "pg_version_constants.h"
#include "distributed/citus_ruleutils.h"
#include "distributed/commands.h"
#include "distributed/commands/utility_hook.h"
#include "distributed/coordinator_protocol.h"
#include "distributed/deparse_shard_query.h"
#include "distributed/deparser.h"
#include "distributed/distributed_planner.h"
#include "distributed/listutils.h"
#include "distributed/local_executor.h"
#include "distributed/metadata_cache.h"
#include "distributed/multi_executor.h"
#include "distributed/multi_partitioning_utils.h"
#include "distributed/multi_physical_planner.h"
#include "distributed/namespace_utils.h"
#include "distributed/relation_access_tracking.h"
#include "distributed/relation_utils.h"
#include "distributed/resource_lock.h"
#include "distributed/version_compat.h"
#include "distributed/worker_manager.h"
#if PG_VERSION_NUM >= PG_VERSION_16
#include "catalog/pg_namespace.h"
#endif
/* Local functions forward declarations for helper functions */
static void ErrorIfCreateIndexHasTooManyColumns(IndexStmt *createIndexStatement);
@ -184,8 +177,6 @@ PreprocessIndexStmt(Node *node, const char *createIndexCommand,
return NIL;
}
EnsureCoordinator();
if (createIndexStatement->idxname == NULL)
{
/*
@ -337,7 +328,7 @@ ExecuteFunctionOnEachTableIndex(Oid relationId, PGIndexProcessor pgIndexProcesso
List *indexIdList = RelationGetIndexList(relation);
Oid indexId = InvalidOid;
foreach_declared_oid(indexId, indexIdList)
foreach_oid(indexId, indexIdList)
{
HeapTuple indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexId));
if (!HeapTupleIsValid(indexTuple))
@ -493,7 +484,6 @@ GenerateCreateIndexDDLJob(IndexStmt *createIndexStatement, const char *createInd
ddlJob->startNewTransaction = createIndexStatement->concurrent;
ddlJob->metadataSyncCommand = createIndexCommand;
ddlJob->taskList = CreateIndexTaskList(createIndexStatement);
ddlJob->warnForPartialFailure = true;
return ddlJob;
}
@ -653,7 +643,6 @@ PreprocessReindexStmt(Node *node, const char *reindexCommand,
"concurrently");
ddlJob->metadataSyncCommand = reindexCommand;
ddlJob->taskList = CreateReindexTaskList(relationId, reindexStatement);
ddlJob->warnForPartialFailure = true;
ddlJobs = list_make1(ddlJob);
}
@ -708,7 +697,7 @@ PreprocessDropIndexStmt(Node *node, const char *dropIndexCommand,
/* check if any of the indexes being dropped belong to a distributed table */
List *objectNameList = NULL;
foreach_declared_ptr(objectNameList, dropIndexStatement->objects)
foreach_ptr(objectNameList, dropIndexStatement->objects)
{
struct DropRelationCallbackState state;
uint32 rvrFlags = RVR_MISSING_OK;
@ -782,7 +771,6 @@ PreprocessDropIndexStmt(Node *node, const char *dropIndexCommand,
ddlJob->metadataSyncCommand = dropIndexCommand;
ddlJob->taskList = DropIndexTaskList(distributedRelationId, distributedIndexId,
dropIndexStatement);
ddlJob->warnForPartialFailure = true;
ddlJobs = list_make1(ddlJob);
}
@ -880,7 +868,7 @@ ErrorIfUnsupportedAlterIndexStmt(AlterTableStmt *alterTableStatement)
/* error out if any of the subcommands are unsupported */
List *commandList = alterTableStatement->cmds;
AlterTableCmd *command = NULL;
foreach_declared_ptr(command, commandList)
foreach_ptr(command, commandList)
{
AlterTableType alterTableType = command->subtype;
@ -932,7 +920,7 @@ CreateIndexTaskList(IndexStmt *indexStmt)
LockShardListMetadata(shardIntervalList, ShareLock);
ShardInterval *shardInterval = NULL;
foreach_declared_ptr(shardInterval, shardIntervalList)
foreach_ptr(shardInterval, shardIntervalList)
{
uint64 shardId = shardInterval->shardId;
@ -947,7 +935,7 @@ CreateIndexTaskList(IndexStmt *indexStmt)
task->dependentTaskList = NULL;
task->anchorShardId = shardId;
task->taskPlacementList = ActiveShardPlacementList(shardId);
task->cannotBeExecutedInTransaction = indexStmt->concurrent;
task->cannotBeExecutedInTransction = indexStmt->concurrent;
taskList = lappend(taskList, task);
@ -977,7 +965,7 @@ CreateReindexTaskList(Oid relationId, ReindexStmt *reindexStmt)
LockShardListMetadata(shardIntervalList, ShareLock);
ShardInterval *shardInterval = NULL;
foreach_declared_ptr(shardInterval, shardIntervalList)
foreach_ptr(shardInterval, shardIntervalList)
{
uint64 shardId = shardInterval->shardId;
@ -992,7 +980,7 @@ CreateReindexTaskList(Oid relationId, ReindexStmt *reindexStmt)
task->dependentTaskList = NULL;
task->anchorShardId = shardId;
task->taskPlacementList = ActiveShardPlacementList(shardId);
task->cannotBeExecutedInTransaction =
task->cannotBeExecutedInTransction =
IsReindexWithParam_compat(reindexStmt, "concurrently");
taskList = lappend(taskList, task);
@ -1067,8 +1055,8 @@ RangeVarCallbackForDropIndex(const RangeVar *rel, Oid relOid, Oid oldRelOid, voi
errmsg("\"%s\" is not an index", rel->relname)));
/* Allow DROP to either table owner or schema owner */
if (!object_ownercheck(RelationRelationId, relOid, GetUserId()) &&
!object_ownercheck(NamespaceRelationId, classform->relnamespace, GetUserId()))
if (!pg_class_ownercheck(relOid, GetUserId()) &&
!pg_namespace_ownercheck(classform->relnamespace, GetUserId()))
{
aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, rel->relname);
}
@ -1115,7 +1103,6 @@ RangeVarCallbackForReindexIndex(const RangeVar *relation, Oid relId, Oid oldRelI
char relkind;
struct ReindexIndexCallbackState *state = arg;
LOCKMODE table_lockmode;
Oid table_oid;
/*
* Lock level here should match table lock in reindex_index() for
@ -1153,24 +1140,13 @@ RangeVarCallbackForReindexIndex(const RangeVar *relation, Oid relId, Oid oldRelI
errmsg("\"%s\" is not an index", relation->relname)));
/* Check permissions */
#if PG_VERSION_NUM >= PG_VERSION_17
table_oid = IndexGetRelation(relId, true);
if (OidIsValid(table_oid))
{
AclResult aclresult = pg_class_aclcheck(table_oid, GetUserId(), ACL_MAINTAIN);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_INDEX, relation->relname);
}
#else
if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
if (!pg_class_ownercheck(relId, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, relation->relname);
#endif
/* Lock heap before index to avoid deadlock. */
if (relId != oldRelId)
{
table_oid = IndexGetRelation(relId, true);
Oid table_oid = IndexGetRelation(relId, true);
/*
* If the OID isn't valid, it means the index was concurrently
@ -1238,7 +1214,7 @@ ErrorIfUnsupportedIndexStmt(IndexStmt *createIndexStatement)
Var *partitionKey = DistPartitionKeyOrError(relationId);
List *indexParameterList = createIndexStatement->indexParams;
IndexElem *indexElement = NULL;
foreach_declared_ptr(indexElement, indexParameterList)
foreach_ptr(indexElement, indexParameterList)
{
const char *columnName = indexElement->name;
@ -1307,7 +1283,7 @@ DropIndexTaskList(Oid relationId, Oid indexId, DropStmt *dropStmt)
LockShardListMetadata(shardIntervalList, ShareLock);
ShardInterval *shardInterval = NULL;
foreach_declared_ptr(shardInterval, shardIntervalList)
foreach_ptr(shardInterval, shardIntervalList)
{
uint64 shardId = shardInterval->shardId;
char *shardIndexName = pstrdup(indexName);
@ -1330,7 +1306,7 @@ DropIndexTaskList(Oid relationId, Oid indexId, DropStmt *dropStmt)
task->dependentTaskList = NULL;
task->anchorShardId = shardId;
task->taskPlacementList = ActiveShardPlacementList(shardId);
task->cannotBeExecutedInTransaction = dropStmt->concurrent;
task->cannotBeExecutedInTransction = dropStmt->concurrent;
taskList = lappend(taskList, task);

Some files were not shown because too many files have changed in this diff Show More