mirror of https://github.com/apache/cassandra
CEP-15 (C*): Messaging and storage engine integration
patch by Blake Eggleston; reviewed by Benedict Elliott Smith, David Capwell for CASSANDRA-17103
This commit is contained in:
parent
e1823e0e22
commit
09c8fa1030
|
|
@ -0,0 +1,37 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!--
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
or more contributor license agreements. See the NOTICE file
|
||||||
|
distributed with this work for additional information
|
||||||
|
regarding copyright ownership. The ASF licenses this file
|
||||||
|
to you under the Apache License, Version 2.0 (the
|
||||||
|
"License"); you may not use this file except in compliance
|
||||||
|
with the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
<project basedir=".." name="apache-cassandra-accord-build"
|
||||||
|
xmlns:if="ant:if">
|
||||||
|
<target name="_build-accord" depends="init">
|
||||||
|
<exec executable="${basedir}/${accord.dir}/gradlew" dir="${basedir}/${accord.dir}" logError="true" failonerror="true" failifexecutionfails="true">
|
||||||
|
<!-- Need to call clean as a version change with a jar that didn't have code changes will not produce a new jar with the new version... so need clean to make sure build is stable -->
|
||||||
|
<arg value="clean" />
|
||||||
|
<arg value="build" />
|
||||||
|
<arg value="copyDependencies" /> <!-- For IDE support -->
|
||||||
|
<arg value="publishToMavenLocal" />
|
||||||
|
<arg value="-x" />
|
||||||
|
<arg value="test" />
|
||||||
|
<arg value="-x" />
|
||||||
|
<arg value="rat" />
|
||||||
|
<arg value="-Paccord_group=org.apache.cassandra" />
|
||||||
|
<arg value="-Paccord_artifactId=cassandra-accord" />
|
||||||
|
<arg value="-Paccord_version=${version}" />
|
||||||
|
</exec>
|
||||||
|
</target>
|
||||||
|
</project>
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
<project basedir="." name="apache-cassandra-checkstyle"
|
<project basedir="." name="apache-cassandra-checkstyle"
|
||||||
xmlns:rat="antlib:org.apache.rat.anttasks">
|
xmlns:rat="antlib:org.apache.rat.anttasks">
|
||||||
|
|
||||||
<target name="init-checkstyle" depends="resolver-retrieve-build,build-project" unless="no-checkstyle">
|
<target name="init-checkstyle" depends="resolver-retrieve-build,_build_subprojects,build-project" unless="no-checkstyle">
|
||||||
<path id="checkstyle.lib.path">
|
<path id="checkstyle.lib.path">
|
||||||
<fileset dir="${test.lib}/jars" includes="*.jar"/>
|
<fileset dir="${test.lib}/jars" includes="*.jar"/>
|
||||||
</path>
|
</path>
|
||||||
|
|
@ -45,7 +45,7 @@
|
||||||
</checkstyle>
|
</checkstyle>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<target name="checkstyle-test" depends="init-checkstyle,resolver-retrieve-build,build-project" description="Run custom checkstyle code analysis on tests" unless="no-checkstyle">
|
<target name="checkstyle-test" depends="init-checkstyle,resolver-retrieve-build,_build_subprojects,build-project" description="Run custom checkstyle code analysis on tests" unless="no-checkstyle">
|
||||||
<property name="checkstyle.log.dir" value="${build.dir}/checkstyle" />
|
<property name="checkstyle.log.dir" value="${build.dir}/checkstyle" />
|
||||||
<property name="checkstyle_test.report.file" value="${checkstyle.log.dir}/checkstyle_report_test.xml"/>
|
<property name="checkstyle_test.report.file" value="${checkstyle.log.dir}/checkstyle_report_test.xml"/>
|
||||||
<mkdir dir="${checkstyle.log.dir}" />
|
<mkdir dir="${checkstyle.log.dir}" />
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,7 @@
|
||||||
<exclude NAME="CHANGES.txt"/>
|
<exclude NAME="CHANGES.txt"/>
|
||||||
<exclude NAME="CASSANDRA-14092.txt"/>
|
<exclude NAME="CASSANDRA-14092.txt"/>
|
||||||
<exclude NAME="debian/TODO"/>
|
<exclude NAME="debian/TODO"/>
|
||||||
|
<exclude NAME="accord_demo.txt"/>
|
||||||
<!-- legal files -->
|
<!-- legal files -->
|
||||||
<exclude NAME="NOTICE.txt"/>
|
<exclude NAME="NOTICE.txt"/>
|
||||||
<exclude NAME="LICENSE.txt"/>
|
<exclude NAME="LICENSE.txt"/>
|
||||||
|
|
|
||||||
|
|
@ -178,7 +178,7 @@
|
||||||
<property name="resolver-ant-tasks.initialized" value="true"/>
|
<property name="resolver-ant-tasks.initialized" value="true"/>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<target name="resolver-retrieve-build" depends="resolver-init,write-poms">
|
<target name="resolver-retrieve-build" depends="resolver-init,write-poms,_build_subprojects">
|
||||||
<resolvepom file="${build.dir}/${final.name}.pom" id="all-pom" />
|
<resolvepom file="${build.dir}/${final.name}.pom" id="all-pom" />
|
||||||
<resolvepom file="${build.dir}/tmp-${final.name}-deps.pom" id="pom-deps" />
|
<resolvepom file="${build.dir}/tmp-${final.name}-deps.pom" id="pom-deps" />
|
||||||
|
|
||||||
|
|
@ -206,7 +206,7 @@
|
||||||
</unzip>
|
</unzip>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<target name="resolver-dist-lib" depends="resolver-retrieve-build">
|
<target name="resolver-dist-lib" depends="resolver-retrieve-build,_build_subprojects">
|
||||||
<resolvepom file="${build.dir}/${final.name}.pom" id="all-pom" />
|
<resolvepom file="${build.dir}/${final.name}.pom" id="all-pom" />
|
||||||
|
|
||||||
<retry retrycount="3" retrydelay="10" >
|
<retry retrycount="3" retrydelay="10" >
|
||||||
|
|
|
||||||
|
|
@ -155,5 +155,10 @@
|
||||||
<groupId>org.bouncycastle</groupId>
|
<groupId>org.bouncycastle</groupId>
|
||||||
<artifactId>bcutil-jdk18on</artifactId>
|
<artifactId>bcutil-jdk18on</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.cassandra</groupId>
|
||||||
|
<artifactId>cassandra-accord</artifactId>
|
||||||
|
<classifier>tests</classifier>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
|
|
||||||
|
|
@ -116,6 +116,10 @@
|
||||||
<groupId>org.mindrot</groupId>
|
<groupId>org.mindrot</groupId>
|
||||||
<artifactId>jbcrypt</artifactId>
|
<artifactId>jbcrypt</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.cassandra</groupId>
|
||||||
|
<artifactId>cassandra-accord</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.airlift</groupId>
|
<groupId>io.airlift</groupId>
|
||||||
<artifactId>airline</artifactId>
|
<artifactId>airline</artifactId>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
# Redirect output to stderr.
|
||||||
|
exec 1>&2
|
||||||
|
|
||||||
|
#set -o xtrace
|
||||||
|
set -o errexit
|
||||||
|
set -o pipefail
|
||||||
|
set -o nounset
|
||||||
|
|
||||||
|
bin="$(cd "$(dirname "$0")" > /dev/null; pwd)"
|
||||||
|
|
||||||
|
_main() {
|
||||||
|
# In case the usage happens at a different layer, make sure to cd to the toplevel
|
||||||
|
local root_dir
|
||||||
|
root_dir="$(git rev-parse --show-toplevel)"
|
||||||
|
cd "$root_dir"
|
||||||
|
|
||||||
|
if [[ ! -e .gitmodules ]]; then
|
||||||
|
# nothing to see here, look away!
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
git submodule update --init --recursive
|
||||||
|
}
|
||||||
|
|
||||||
|
_main "$@"
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
post-checkout
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
##
|
||||||
|
## When working with submodules the top level project (Apache Cassandra) needs to commit all submodule
|
||||||
|
## changes so the top level knows what SHA to use. When working in a development environment it is
|
||||||
|
## common that multiple commits will exist in both projects, if the submodule has its history
|
||||||
|
## rewritten, then historic top level commits are no longer valid unless the SHAs are pushed to a
|
||||||
|
## remote repo; this is what the script attempts to do, make sure all SHAs added to the
|
||||||
|
## Apache Cassandra are backed up to a remote repo to make the Cassandra SHA buildable.
|
||||||
|
##
|
||||||
|
|
||||||
|
# Redirect output to stderr.
|
||||||
|
exec 1>&2
|
||||||
|
|
||||||
|
|
||||||
|
#set -o xtrace
|
||||||
|
set -o errexit
|
||||||
|
set -o pipefail
|
||||||
|
set -o nounset
|
||||||
|
|
||||||
|
bin="$(cd "$(dirname "$0")" > /dev/null; pwd)"
|
||||||
|
|
||||||
|
_log() {
|
||||||
|
echo -e "[pre-commit]\t$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
error() {
|
||||||
|
_log "$@" 1>&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Status Table
|
||||||
|
# A Added
|
||||||
|
# C Copied
|
||||||
|
# D Deleted
|
||||||
|
# M Modified
|
||||||
|
# R Renamed
|
||||||
|
# T Type Changed (i.e. regular file, symlink, submodule, …<200b>)
|
||||||
|
# U Unmerged
|
||||||
|
# X Unknown
|
||||||
|
# B Broken
|
||||||
|
_main() {
|
||||||
|
# In case the usage happens at a different layer, make sure to cd to the toplevel
|
||||||
|
local root_dir
|
||||||
|
root_dir="$(git rev-parse --show-toplevel)"
|
||||||
|
cd "$root_dir"
|
||||||
|
|
||||||
|
[[ ! -e .gitmodules ]] && return 0
|
||||||
|
local enabled=$(git config --bool cassandra.pre-commit.verify-submodules.enabled || echo true)
|
||||||
|
[ "$enabled" == "false" ] && return 0
|
||||||
|
local submodules=( $(git config --file .gitmodules --get-regexp path | awk '{ print $2 }') )
|
||||||
|
|
||||||
|
local is_submodule=false
|
||||||
|
local git_sub_dir
|
||||||
|
local git_sha
|
||||||
|
while read status file; do
|
||||||
|
is_submodule=false
|
||||||
|
for to_check in "${submodules[*]}"; do
|
||||||
|
if [[ "$to_check" == "$file" ]]; then
|
||||||
|
is_submodule=true
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if $is_submodule; then
|
||||||
|
local enabled=$(git config --bool cassandra.pre-commit.verify-submodule-${file}.enabled || echo true)
|
||||||
|
[ "$enabled" == "false" ] && continue
|
||||||
|
_log "Submodule detected: ${file} with status ${status}; attempting a push"
|
||||||
|
_log "\tTo disable pushes, run"
|
||||||
|
_log "\t\tgit config --local cassandra.pre-commit.verify-submodules.enabled false"
|
||||||
|
_log "\tOr"
|
||||||
|
_log "\t\tgit config --local cassandra.pre-commit.verify-submodule-${file}.enabled false"
|
||||||
|
set -x
|
||||||
|
git_sub_dir="${file}/.git"
|
||||||
|
branch="$(git config -f .gitmodules "submodule.${file}.branch")"
|
||||||
|
[[ -z "${branch:-}" ]] && error "Submodule ${file} does not define a branch"
|
||||||
|
git_sha="$(git --git-dir "${git_sub_dir}" rev-parse HEAD)"
|
||||||
|
git --git-dir "${git_sub_dir}" fetch origin
|
||||||
|
git --git-dir "${git_sub_dir}" branch "origin/${branch}" --contains "${git_sha}" || error "Git commit ${git_sha} not found in $(git remote get-url origin) on branch ${branch}"
|
||||||
|
fi
|
||||||
|
done < <(git diff --cached --name-status)
|
||||||
|
}
|
||||||
|
|
||||||
|
_main "$@"
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
# Redirect output to stderr.
|
||||||
|
exec 1>&2
|
||||||
|
|
||||||
|
#set -o xtrace
|
||||||
|
set -o errexit
|
||||||
|
set -o pipefail
|
||||||
|
set -o nounset
|
||||||
|
|
||||||
|
bin="$(cd "$(dirname "$0")" > /dev/null; pwd)"
|
||||||
|
|
||||||
|
_main() {
|
||||||
|
# In case the usage happens at a different layer, make sure to cd to the toplevel
|
||||||
|
local root_dir
|
||||||
|
root_dir="$(git rev-parse --show-toplevel)"
|
||||||
|
cd "$root_dir"
|
||||||
|
|
||||||
|
if [[ ! -e .gitmodules ]]; then
|
||||||
|
# nothing to see here, look away!
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local -r cmd='
|
||||||
|
branch="$(git rev-parse --abbrev-ref HEAD)"
|
||||||
|
[[ "$branch" == "HEAD" ]] && exit 0
|
||||||
|
|
||||||
|
default_remote="$(git config --local --get branch."${branch}".remote || true)"
|
||||||
|
remote="${default_remote:-origin}"
|
||||||
|
|
||||||
|
git push --atomic "$remote" "$branch"
|
||||||
|
'
|
||||||
|
git submodule foreach --recursive "$cmd"
|
||||||
|
}
|
||||||
|
|
||||||
|
_main "$@"
|
||||||
|
|
@ -0,0 +1,117 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
#set -o xtrace
|
||||||
|
set -o errexit
|
||||||
|
set -o pipefail
|
||||||
|
set -o nounset
|
||||||
|
|
||||||
|
bin="$(cd "$(dirname "$0")" > /dev/null; pwd)"
|
||||||
|
|
||||||
|
install_template_script() {
|
||||||
|
local -r name="$1"
|
||||||
|
local -r d_dir="$2"
|
||||||
|
|
||||||
|
cat <<EOF > "$name"
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# This script is autogenerated by the Apache Cassandra build; DO NOT CHANGE!
|
||||||
|
# When this script is not found it will be installed automatically by the build
|
||||||
|
# If an existing script is found, that script will be reloated under ${d_dir} as 000-original.sh
|
||||||
|
|
||||||
|
# Redirect output to stderr.
|
||||||
|
exec 1>&2
|
||||||
|
|
||||||
|
# Find all scripts to run
|
||||||
|
for path in \$(find "$d_dir" -name '*.sh' | perl -e "print sort{(split '/', \\\$a)[-1] <=> (split '/', \\\$b)[-1]}<>"); do
|
||||||
|
"\$path" "\$@"
|
||||||
|
done
|
||||||
|
EOF
|
||||||
|
chmod a+x "$name"
|
||||||
|
}
|
||||||
|
|
||||||
|
install_hook() {
|
||||||
|
local -r git_dir="$1"
|
||||||
|
local -r hooks_dir="${git_dir}/hooks"
|
||||||
|
local -r name="$2"
|
||||||
|
local -r d_dir="${hooks_dir}/${name}.d"
|
||||||
|
local -r trigger_on_install=$3
|
||||||
|
|
||||||
|
mkdir "${d_dir}" &> /dev/null || true
|
||||||
|
local -r script_name="${hooks_dir}/${name}"
|
||||||
|
local installed=true
|
||||||
|
if [[ -e "$script_name" ]]; then
|
||||||
|
# was the script already installed?
|
||||||
|
if ! grep "This script is autogenerated by the Apache Cassandra build" "$script_name" &> /dev/null ; then
|
||||||
|
echo "$script_name found, but was not generated by the Apache Cassandra build; please remove or move to ${d_dir}/000-original.sh; creating and moving to ${d_dir} will cause it to run as expected, but won't conflict with hooks this build adds" 1>&2
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
installed=false
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
# install all hooks
|
||||||
|
cp "$bin"/git-hooks/"${name}"/* "$d_dir"/
|
||||||
|
|
||||||
|
# install coordinator hook
|
||||||
|
install_template_script "$script_name" "$d_dir"
|
||||||
|
if $installed && $trigger_on_install ; then
|
||||||
|
echo "Running script $script_name"
|
||||||
|
"$script_name"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_install_hooks() {
|
||||||
|
local git_dir
|
||||||
|
# make sure to use --git-common-dir and not --git-dir to support worktrees
|
||||||
|
git_dir="$(git rev-parse --git-common-dir 2> /dev/null || true)"
|
||||||
|
if [[ -z "${git_dir:-}" ]]; then
|
||||||
|
# not in a git repo, noop
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# make sure hooks directory exists; does not exist by default for worktrees
|
||||||
|
mkdir -p "${git_dir}/hooks" &> /dev/null || true
|
||||||
|
|
||||||
|
install_hook "$git_dir" "post-checkout" true
|
||||||
|
install_hook "$git_dir" "post-switch" false
|
||||||
|
install_hook "$git_dir" "pre-commit" false
|
||||||
|
install_hook "$git_dir" "pre-push" false
|
||||||
|
}
|
||||||
|
|
||||||
|
_git_config_set() {
|
||||||
|
local -r name="$1"
|
||||||
|
# only care about rc
|
||||||
|
git config --local --get "$name" &> /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
_install_configs() {
|
||||||
|
# when doing pull, this makes sure submodules are updated
|
||||||
|
_git_config_set submodule.recurse || git config --local submodule.recurse true
|
||||||
|
}
|
||||||
|
|
||||||
|
_main() {
|
||||||
|
local git_dir
|
||||||
|
# make sure to use --git-common-dir and not --git-dir to support worktrees
|
||||||
|
git_dir="$(git rev-parse --git-common-dir 2> /dev/null || true)"
|
||||||
|
# not in a git repo, noop
|
||||||
|
[[ -z "${git_dir:-}" ]] && return 0
|
||||||
|
|
||||||
|
_install_configs
|
||||||
|
_install_hooks
|
||||||
|
}
|
||||||
|
|
||||||
|
_main "$@"
|
||||||
|
|
@ -715,6 +715,42 @@
|
||||||
<artifactId>jbcrypt</artifactId>
|
<artifactId>jbcrypt</artifactId>
|
||||||
<version>0.4</version>
|
<version>0.4</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.cassandra</groupId>
|
||||||
|
<artifactId>cassandra-accord</artifactId>
|
||||||
|
<version>@version@</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>org.apache.cassandra</artifactId>
|
||||||
|
<groupId>cassandra-all</groupId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.cassandra</groupId>
|
||||||
|
<artifactId>cassandra-accord</artifactId>
|
||||||
|
<version>@version@</version>
|
||||||
|
<classifier>tests</classifier>
|
||||||
|
<scope>test</scope>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>org.junit.jupiter</artifactId>
|
||||||
|
<groupId>junit-jupiter-api</groupId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>org.junit.jupiter</artifactId>
|
||||||
|
<groupId>junit-jupiter-engine</groupId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>ch.qos.logback</artifactId>
|
||||||
|
<groupId>logback-classic</groupId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>org.apache.cassandra</artifactId>
|
||||||
|
<groupId>cassandra-all</groupId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.airlift</groupId>
|
<groupId>io.airlift</groupId>
|
||||||
<artifactId>airline</artifactId>
|
<artifactId>airline</artifactId>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
#set -o xtrace
|
||||||
|
set -o errexit
|
||||||
|
set -o pipefail
|
||||||
|
set -o nounset
|
||||||
|
|
||||||
|
_main() {
|
||||||
|
local home
|
||||||
|
home="$(git rev-parse --show-toplevel)"
|
||||||
|
cd "$home"
|
||||||
|
|
||||||
|
git submodule status modules/accord
|
||||||
|
echo "Is this the correct SHA? [y/n; default=y]"
|
||||||
|
read correct
|
||||||
|
if [[ "${correct:-y}" != "y" ]]; then
|
||||||
|
echo "Please update Accord's SHA and try again"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
git commit -m "Change Accord to $(cd modules/accord; git log -1 --format='%h: %B')" modules/accord
|
||||||
|
}
|
||||||
|
|
||||||
|
_main "$@"
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
#set -o xtrace
|
||||||
|
set -o errexit
|
||||||
|
set -o pipefail
|
||||||
|
set -o nounset
|
||||||
|
|
||||||
|
bin="$(cd "$(dirname "$0")" > /dev/null; pwd)"
|
||||||
|
|
||||||
|
"$bin"/change-submodule.sh modules/accord 'https://github.com/apache/cassandra-accord.git' trunk
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
#set -o xtrace
|
||||||
|
set -o errexit
|
||||||
|
set -o pipefail
|
||||||
|
set -o nounset
|
||||||
|
|
||||||
|
_usage() {
|
||||||
|
cat <<EOF
|
||||||
|
Usage: $(basename $0) [submodule path] [git repo URL] [git branch]
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
Switch to branch "immutable-state"
|
||||||
|
.build/sh/change-submodule.sh modules/accord ../cassandra-accord.git immutable-state
|
||||||
|
EOF
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_main() {
|
||||||
|
if [[ $# -ne 3 ]]; then
|
||||||
|
_usage
|
||||||
|
fi
|
||||||
|
local -r path="$1"
|
||||||
|
local -r url="$2"
|
||||||
|
local -r branch="$3"
|
||||||
|
|
||||||
|
local home
|
||||||
|
home="$(git rev-parse --show-toplevel)"
|
||||||
|
cd "$home"
|
||||||
|
|
||||||
|
git submodule set-url "${path}" "${url}"
|
||||||
|
git submodule set-branch --branch "${branch}" "${path}"
|
||||||
|
git submodule update --remote
|
||||||
|
}
|
||||||
|
|
||||||
|
_main "$@"
|
||||||
|
|
@ -0,0 +1,117 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
#set -o xtrace
|
||||||
|
set -o errexit
|
||||||
|
set -o pipefail
|
||||||
|
set -o nounset
|
||||||
|
|
||||||
|
error() {
|
||||||
|
echo -e "$*" 1>&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_usage() {
|
||||||
|
cat <<EOF
|
||||||
|
Usage: $(basename $0) (options)* (submodule)*
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--jira JIRA used for development, will checkout if not done yet
|
||||||
|
-h|--help This help page
|
||||||
|
EOF
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_is_main_branch() {
|
||||||
|
local -r name="$1"
|
||||||
|
[[ "$name" == cassandra-* ]] && return 0
|
||||||
|
[[ "$name" == "trunk" ]] && return 0
|
||||||
|
[[ "$name" == "cep-15-accord" ]] && return 0
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_main() {
|
||||||
|
local home
|
||||||
|
home="$(git rev-parse --show-toplevel)"
|
||||||
|
cd "$home"
|
||||||
|
|
||||||
|
local branch
|
||||||
|
branch="$(git rev-parse --abbrev-ref HEAD)"
|
||||||
|
# loop over args, executing as in order of execution
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
-h|--help)
|
||||||
|
_usage
|
||||||
|
;;
|
||||||
|
--jira)
|
||||||
|
if [[ "$2" != "$branch" ]]; then
|
||||||
|
git checkout -b "$2"
|
||||||
|
branch="$2"
|
||||||
|
fi
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
while _is_main_branch "$branch" ; do
|
||||||
|
echo "Currently on $branch, which does not look like a development brarnch; what JIRA are you working on? "
|
||||||
|
read jira
|
||||||
|
if [[ ! -z "${jira:-}" ]]; then
|
||||||
|
git checkout -b "$jira"
|
||||||
|
branch="$jira"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
local submodules
|
||||||
|
submodules=( $(git config --file "$home"/.gitmodules --get-regexp path | awk '{ print $2 }') )
|
||||||
|
local to_change=()
|
||||||
|
if [[ $# -gt 0 ]]; then
|
||||||
|
local exists
|
||||||
|
for a in "$@"; do
|
||||||
|
exists=false
|
||||||
|
for sub in "${submodules[@]}"; do
|
||||||
|
if [[ "$sub" == "$a" ]]; then
|
||||||
|
exists=true
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
[ "$exists" == false ] && error "git submodule $a does not exist"
|
||||||
|
to_change+=( "$a" )
|
||||||
|
done
|
||||||
|
else
|
||||||
|
for sub in "${submodules[@]}"; do
|
||||||
|
to_change+=( "$sub" )
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
local submodule_branch
|
||||||
|
local name
|
||||||
|
for path in "${to_change[@]}"; do
|
||||||
|
name="$(basename "$path")"
|
||||||
|
git submodule set-url "${path}" "../cassandra-${name}.git"
|
||||||
|
git submodule set-branch --branch "${branch}" "${path}"
|
||||||
|
cd "$path"
|
||||||
|
submodule_branch="$(git rev-parse --abbrev-ref HEAD)"
|
||||||
|
if [[ "${submodule_branch}" != "${branch}" ]]; then
|
||||||
|
git checkout -b "$branch"
|
||||||
|
fi
|
||||||
|
cd -
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
_main "$@"
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
[submodule "modules/accord"]
|
||||||
|
path = modules/accord
|
||||||
|
url = https://github.com/apache/cassandra-accord.git
|
||||||
|
branch = trunk
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
5.1
|
5.1
|
||||||
|
* General Purpose Transactions (Accord) [CEP-15] (CASSANDRA-17092)
|
||||||
* Improve performance when getting writePlacementsAllSettled from ClusterMetadata (CASSANDRA-20526)
|
* Improve performance when getting writePlacementsAllSettled from ClusterMetadata (CASSANDRA-20526)
|
||||||
* Add nodetool command to dump the contents of the system_views.{cluster_metadata_log, cluster_metadata_directory} tables (CASSANDRA-20525)
|
* Add nodetool command to dump the contents of the system_views.{cluster_metadata_log, cluster_metadata_directory} tables (CASSANDRA-20525)
|
||||||
* Fix TreeMap race in CollectionVirtualTableAdapter causing us to lose rows in the virtual table (CASSANDRA-20524)
|
* Fix TreeMap race in CollectionVirtualTableAdapter causing us to lose rows in the virtual table (CASSANDRA-20524)
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,37 @@ In fact, this repository is a GitHub mirror of [the official repo](https://gitbo
|
||||||
|
|
||||||
Use [Cassandra JIRA](https://issues.apache.org/jira/browse/CASSANDRA/) to create an issue, then either attach a patch or post a link to a GitHub branch with your changes.
|
Use [Cassandra JIRA](https://issues.apache.org/jira/browse/CASSANDRA/) to create an issue, then either attach a patch or post a link to a GitHub branch with your changes.
|
||||||
|
|
||||||
|
# Working with submodules
|
||||||
|
|
||||||
|
Apache Cassandra uses git submodules for a set of dependencies, this is to make cross cutting changes easier for developers. When working on such changes, there are a set of scripts to help with the process.
|
||||||
|
|
||||||
|
## Local Development
|
||||||
|
|
||||||
|
When starting a development branch, the following will change all submodules to a new branch based off the JIRA
|
||||||
|
|
||||||
|
```
|
||||||
|
$ .build/sh/development-switch.sh --jira CASSANDRA-<number>
|
||||||
|
```
|
||||||
|
|
||||||
|
When changes are made to a submodule (such as to accord), you need to commit and update the reference in Apache Cassandra
|
||||||
|
|
||||||
|
```
|
||||||
|
$ (cd modules/accord ; git commit -am 'Saving progress')
|
||||||
|
$ .build/sh/bump-accord.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## Commit and Merge Process
|
||||||
|
|
||||||
|
Due to the nature of submodules, the changes to the submodules must be committed and pushed before the changes to Apache Cassandra; these are different repositories so git's `--atomic` does not prevent conflicts from concurrent merges; the basic process is as follows:
|
||||||
|
|
||||||
|
* Follow the normal merge process for the submodule
|
||||||
|
* Update Apache Cassandra's submodule entry to point to the newly committed change; follow the Accord example below for an example
|
||||||
|
|
||||||
|
```
|
||||||
|
$ .build/sh/change-submodule-accord.sh
|
||||||
|
$ .build/sh/bump-accord.sh
|
||||||
|
```
|
||||||
|
|
||||||
# Useful Links
|
# Useful Links
|
||||||
|
|
||||||
- How you can contribute to Apache Cassandra [presentation](http://www.slideshare.net/yukim/cassandrasummit2013) by Yuki Morishita
|
- How you can contribute to Apache Cassandra [presentation](http://www.slideshare.net/yukim/cassandrasummit2013) by Yuki Morishita
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
|
||||||
|
ccm create accord-cql-poc -n 3
|
||||||
|
ccm start
|
||||||
|
|
||||||
|
bin/cqlsh -e "create keyspace ks with replication={'class':'SimpleStrategy', 'replication_factor':3};"
|
||||||
|
bin/cqlsh -e "create table ks.tbl1 (k int primary key, v int);"
|
||||||
|
bin/cqlsh -e "create table ks.tbl2 (k int primary key, v int);"
|
||||||
|
|
||||||
|
bin/nodetool -h 0000:0000:0000:0000:0000:ffff:7f00:0001 -p 7100 createepochunsafe
|
||||||
|
bin/nodetool -h 0000:0000:0000:0000:0000:ffff:7f00:0001 -p 7200 createepochunsafe
|
||||||
|
bin/nodetool -h 0000:0000:0000:0000:0000:ffff:7f00:0001 -p 7300 createepochunsafe
|
||||||
|
|
||||||
|
BEGIN TRANSACTION
|
||||||
|
LET row1 = (SELECT * FROM ks.tbl1 WHERE k = 1);
|
||||||
|
SELECT row1.v;
|
||||||
|
IF row1 IS NULL THEN
|
||||||
|
INSERT INTO ks.tbl1 (k, v) VALUES (1, 2);
|
||||||
|
END IF
|
||||||
|
COMMIT TRANSACTION;
|
||||||
28
build.xml
28
build.xml
|
|
@ -100,6 +100,8 @@
|
||||||
the user specifies the tmp.dir property -->
|
the user specifies the tmp.dir property -->
|
||||||
<property name="tmp.dir" value="${build.dir}/tmp"/>
|
<property name="tmp.dir" value="${build.dir}/tmp"/>
|
||||||
|
|
||||||
|
<property name="ant.build.src" value="${basedir}/.build" />
|
||||||
|
|
||||||
<property name="doc.dir" value="${basedir}/doc"/>
|
<property name="doc.dir" value="${basedir}/doc"/>
|
||||||
|
|
||||||
<condition property="version" value="${base.version}">
|
<condition property="version" value="${base.version}">
|
||||||
|
|
@ -109,8 +111,12 @@
|
||||||
<property name="version.properties.dir"
|
<property name="version.properties.dir"
|
||||||
value="${build.src.resources}/org/apache/cassandra/config/" />
|
value="${build.src.resources}/org/apache/cassandra/config/" />
|
||||||
<property name="final.name" value="${ant.project.name}-${version}"/>
|
<property name="final.name" value="${ant.project.name}-${version}"/>
|
||||||
|
<property name="accord.dir" value="modules/accord" />
|
||||||
|
<!-- The reason not to use ant.project.name is we publish as "cassandra-accord" so the file names won't be named apache-cassandra-accord -->
|
||||||
|
<property name="accord.final.name" value="cassandra-accord-${version}"/>
|
||||||
|
|
||||||
<property name="local.repository" value="${user.home}/.m2/repository" />
|
<property name="local.repository" value="${user.home}/.m2/repository" />
|
||||||
|
<property name="accord.local.repository" value="${local.repository}/org/apache/cassandra/cassandra-accord/${version}" />
|
||||||
|
|
||||||
<!-- details of how and which Maven repository we publish to -->
|
<!-- details of how and which Maven repository we publish to -->
|
||||||
<property name="maven.version" value="3.0.3" />
|
<property name="maven.version" value="3.0.3" />
|
||||||
|
|
@ -396,6 +402,7 @@
|
||||||
<mkdir dir="${build.dir.lib}"/>
|
<mkdir dir="${build.dir.lib}"/>
|
||||||
<mkdir dir="${jacoco.export.dir}"/>
|
<mkdir dir="${jacoco.export.dir}"/>
|
||||||
<mkdir dir="${jacoco.partials.dir}"/>
|
<mkdir dir="${jacoco.partials.dir}"/>
|
||||||
|
<exec executable="${ant.build.src}/git/install-git-defaults.sh" osfamily="unix" dir="${basedir}" logError="true" failonerror="true" failifexecutionfails="true" />
|
||||||
|
|
||||||
<!-- Set up jdk specific properties -->
|
<!-- Set up jdk specific properties -->
|
||||||
<javac includes="**/JdkProperties.java,**/SetSystemProperty.java" srcdir="test/anttasks" destdir="${test.classes}" includeantruntime="true" source="${java.default}" target="${java.default}">
|
<javac includes="**/JdkProperties.java,**/SetSystemProperty.java" srcdir="test/anttasks" destdir="${test.classes}" includeantruntime="true" source="${java.default}" target="${java.default}">
|
||||||
|
|
@ -527,7 +534,8 @@
|
||||||
<!--
|
<!--
|
||||||
The build target builds all the .class files
|
The build target builds all the .class files
|
||||||
-->
|
-->
|
||||||
<target name="build" depends="resolver-retrieve-build,build-project" description="Compile Cassandra classes"/>
|
<target name="_build_subprojects" depends="_build-accord" description="Builds all subprojects needed for the build" />
|
||||||
|
<target name="build" depends="resolver-retrieve-build,_build_subprojects,build-project" description="Compile Cassandra classes"/>
|
||||||
<target name="codecoverage" depends="jacoco-run,jacoco-report" description="Create code coverage report"/>
|
<target name="codecoverage" depends="jacoco-run,jacoco-report" description="Create code coverage report"/>
|
||||||
|
|
||||||
<target name="_build_java">
|
<target name="_build_java">
|
||||||
|
|
@ -973,6 +981,9 @@
|
||||||
<exclude name=".externalToolBuilders/**" />
|
<exclude name=".externalToolBuilders/**" />
|
||||||
<!-- exclude NetBeans files -->
|
<!-- exclude NetBeans files -->
|
||||||
<exclude name="ide/nbproject/private/**" />
|
<exclude name="ide/nbproject/private/**" />
|
||||||
|
<!-- Accord build -->
|
||||||
|
<exclude name="${accord.dir}/*/build/**" />
|
||||||
|
<exclude name="${accord.dir}/**/.gradle/**" />
|
||||||
</tarfileset>
|
</tarfileset>
|
||||||
|
|
||||||
<!-- python driver -->
|
<!-- python driver -->
|
||||||
|
|
@ -992,6 +1003,7 @@
|
||||||
<include name="tools/bin/*"/>
|
<include name="tools/bin/*"/>
|
||||||
<exclude name="tools/bin/*.in.sh" />
|
<exclude name="tools/bin/*.in.sh" />
|
||||||
<include name=".build/**/*.sh"/>
|
<include name=".build/**/*.sh"/>
|
||||||
|
<include name="${accord.dir}/gradlew" />
|
||||||
</tarfileset>
|
</tarfileset>
|
||||||
</tar>
|
</tar>
|
||||||
|
|
||||||
|
|
@ -2061,6 +2073,7 @@
|
||||||
<target name="mvn-install"
|
<target name="mvn-install"
|
||||||
depends="jar,sources-jar,javadoc-jar"
|
depends="jar,sources-jar,javadoc-jar"
|
||||||
description="Installs the artifacts in the Maven Local Repository">
|
description="Installs the artifacts in the Maven Local Repository">
|
||||||
|
<!-- This logic does not install cassandra-accord as the accord submodule already did so -->
|
||||||
|
|
||||||
<!-- the parent -->
|
<!-- the parent -->
|
||||||
<install pomFile="${build.dir}/${final.name}-parent.pom"
|
<install pomFile="${build.dir}/${final.name}-parent.pom"
|
||||||
|
|
@ -2082,6 +2095,18 @@
|
||||||
<target name="publish"
|
<target name="publish"
|
||||||
depends="mvn-install,artifacts"
|
depends="mvn-install,artifacts"
|
||||||
description="Publishes the artifacts to the Maven repository">
|
description="Publishes the artifacts to the Maven repository">
|
||||||
|
<!-- Accord -->
|
||||||
|
<deploy pomFile="${accord.local.repository}/${accord.final.name}.pom"
|
||||||
|
file="${accord.local.repository}/${accord.final.name}.pom"
|
||||||
|
packaging="pom"/>
|
||||||
|
<deploy pomFile="${accord.local.repository}/${accord.final.name}.pom"
|
||||||
|
file="${accord.local.repository}/${accord.final.name}.jar" />
|
||||||
|
<deploy pomFile="${accord.local.repository}/${accord.final.name}.pom"
|
||||||
|
file="${accord.local.repository}/${accord.final.name}-sources.jar"
|
||||||
|
classifier="sources"/>
|
||||||
|
<deploy pomFile="${accord.local.repository}/${accord.final.name}.pom"
|
||||||
|
file="${accord.local.repository}/${accord.final.name}-javadoc.jar"
|
||||||
|
classifier="javadoc"/>
|
||||||
|
|
||||||
<!-- the parent -->
|
<!-- the parent -->
|
||||||
<deploy pomFile="${build.dir}/${final.name}-parent.pom"
|
<deploy pomFile="${build.dir}/${final.name}-parent.pom"
|
||||||
|
|
@ -2112,4 +2137,5 @@
|
||||||
<import file="${build.helpers.dir}/build-cqlsh.xml"/>
|
<import file="${build.helpers.dir}/build-cqlsh.xml"/>
|
||||||
<import file="${build.helpers.dir}/build-bench.xml"/>
|
<import file="${build.helpers.dir}/build-bench.xml"/>
|
||||||
<import file="${build.helpers.dir}/build-sonar.xml"/>
|
<import file="${build.helpers.dir}/build-sonar.xml"/>
|
||||||
|
<import file="${build.helpers.dir}/build-accord.xml" />
|
||||||
</project>
|
</project>
|
||||||
|
|
|
||||||
|
|
@ -2199,6 +2199,9 @@ drop_compact_storage_enabled: false
|
||||||
# Whether or not USE <keyspace> is allowed. This is enabled by default to avoid failure on upgrade.
|
# Whether or not USE <keyspace> is allowed. This is enabled by default to avoid failure on upgrade.
|
||||||
#use_statements_enabled: true
|
#use_statements_enabled: true
|
||||||
|
|
||||||
|
# Enables the execution of Accord (multi-key) transactions on this node.
|
||||||
|
accord_transactions_enabled: false
|
||||||
|
|
||||||
# When the client triggers a protocol exception or unknown issue (Cassandra bug) we increment
|
# When the client triggers a protocol exception or unknown issue (Cassandra bug) we increment
|
||||||
# a client metric showing this; this logic will exclude specific subnets from updating these
|
# a client metric showing this; this logic will exclude specific subnets from updating these
|
||||||
# metrics
|
# metrics
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,16 @@
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/data"/>
|
<excludeFolder url="file://$MODULE_DIR$/data"/>
|
||||||
<excludeFolder url="file://$MODULE_DIR$/logs"/>
|
<excludeFolder url="file://$MODULE_DIR$/logs"/>
|
||||||
|
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/modules/accord/accord-core/src/main/java" isTestSource="false"/>
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/modules/accord/accord-core/src/main/resources" ype="java-resource"/>
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/modules/accord/accord-core/src/test/java" isTestSource="true"/>
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/modules/accord/accord-core/src/test/resources" ype="java-test-resource"/>
|
||||||
|
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/modules/accord/accord-maelstrom/src/main/java" isTestSource="false"/>
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/modules/accord/accord-maelstrom/src/main/resources" ype="java-resource"/>
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/modules/accord/accord-maelstrom/src/test/java" isTestSource="true"/>
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/modules/accord/accord-maelstrom/src/test/resources" ype="java-test-resource"/>
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="inheritedJdk" />
|
<orderEntry type="inheritedJdk" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
|
@ -56,6 +66,8 @@
|
||||||
<library>
|
<library>
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="file://$MODULE_DIR$/build/lib/jars" />
|
<root url="file://$MODULE_DIR$/build/lib/jars" />
|
||||||
|
<root url="file://$MODULE_DIR$/modules/accord/accord-core/build/dependencies/main/libs" />
|
||||||
|
<root url="file://$MODULE_DIR$/modules/accord/accord-maelstrom/build/dependencies/main/libs" />
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC />
|
<JAVADOC />
|
||||||
<SOURCES>
|
<SOURCES>
|
||||||
|
|
@ -63,12 +75,17 @@
|
||||||
</SOURCES>
|
</SOURCES>
|
||||||
<jarDirectory url="file://$MODULE_DIR$/build/lib/jars" recursive="false" />
|
<jarDirectory url="file://$MODULE_DIR$/build/lib/jars" recursive="false" />
|
||||||
<jarDirectory url="file://$MODULE_DIR$/build/lib/sources" recursive="false" type="SOURCES" />
|
<jarDirectory url="file://$MODULE_DIR$/build/lib/sources" recursive="false" type="SOURCES" />
|
||||||
|
|
||||||
|
<jarDirectory url="file://$MODULE_DIR$/modules/accord/accord-core/build/dependencies/main/libs" recursive="false" />
|
||||||
|
<jarDirectory url="file://$MODULE_DIR$/modules/accord/accord-maelstrom/build/dependencies/main/libs" recursive="false" />
|
||||||
</library>
|
</library>
|
||||||
</orderEntry>
|
</orderEntry>
|
||||||
<orderEntry type="module-library" scope="TEST">
|
<orderEntry type="module-library" scope="TEST">
|
||||||
<library>
|
<library>
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="file://$MODULE_DIR$/build/test/lib/jars" />
|
<root url="file://$MODULE_DIR$/build/test/lib/jars" />
|
||||||
|
<root url="file://$MODULE_DIR$/modules/accord/accord-core/build/dependencies/test/libs" />
|
||||||
|
<root url="file://$MODULE_DIR$/modules/accord/accord-maelstrom/build/dependencies/test/libs" />
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC />
|
<JAVADOC />
|
||||||
<SOURCES>
|
<SOURCES>
|
||||||
|
|
@ -76,6 +93,9 @@
|
||||||
</SOURCES>
|
</SOURCES>
|
||||||
<jarDirectory url="file://$MODULE_DIR$/build/test/lib/jars" recursive="false" />
|
<jarDirectory url="file://$MODULE_DIR$/build/test/lib/jars" recursive="false" />
|
||||||
<jarDirectory url="file://$MODULE_DIR$/build/test/lib/sources" recursive="false" type="SOURCES" />
|
<jarDirectory url="file://$MODULE_DIR$/build/test/lib/sources" recursive="false" type="SOURCES" />
|
||||||
|
|
||||||
|
<jarDirectory url="file://$MODULE_DIR$/modules/accord/accord-core/build/dependencies/test/libs" recursive="false" />
|
||||||
|
<jarDirectory url="file://$MODULE_DIR$/modules/accord/accord-maelstrom/build/dependencies/test/libs" recursive="false" />
|
||||||
</library>
|
</library>
|
||||||
</orderEntry>
|
</orderEntry>
|
||||||
</component>
|
</component>
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="VcsDirectoryMappings">
|
<component name="VcsDirectoryMappings">
|
||||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
<mapping directory="$PROJECT_DIR$/modules/accord" vcs="Git" />
|
||||||
</component>
|
</component>
|
||||||
<component name="IssueNavigationConfiguration">
|
<component name="IssueNavigationConfiguration">
|
||||||
<option name="links">
|
<option name="links">
|
||||||
|
|
@ -13,4 +14,4 @@
|
||||||
</list>
|
</list>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
|
|
|
||||||
|
|
@ -183,24 +183,38 @@
|
||||||
<option name="TEST_OBJECT" value="class" />
|
<option name="TEST_OBJECT" value="class" />
|
||||||
<option name="VM_PARAMETERS" value="
|
<option name="VM_PARAMETERS" value="
|
||||||
-Dcassandra.config=file://$PROJECT_DIR$/test/conf/cassandra.yaml
|
-Dcassandra.config=file://$PROJECT_DIR$/test/conf/cassandra.yaml
|
||||||
|
-Dcassandra.debugrefcount=false
|
||||||
-Dcassandra.logdir=$PROJECT_DIR$/build/test/logs
|
-Dcassandra.logdir=$PROJECT_DIR$/build/test/logs
|
||||||
|
-Dcassandra.memtable_row_overhead_computation_step=100
|
||||||
-Dcassandra.reads.thresholds.coordinator.defensive_checks_enabled=true
|
-Dcassandra.reads.thresholds.coordinator.defensive_checks_enabled=true
|
||||||
-Dcassandra.ring_delay_ms=1000
|
-Dcassandra.ring_delay_ms=10000
|
||||||
-Dcassandra.skip_sync=true
|
-Dcassandra.skip_sync=true
|
||||||
-Dcassandra.strict.runtime.checks=true
|
-Dcassandra.strict.runtime.checks=true
|
||||||
-Dcassandra.test.flush_local_schema_changes=false
|
-Dcassandra.test.flush_local_schema_changes=false
|
||||||
-Dcassandra.test.messagingService.nonGracefulShutdown=true
|
-Dcassandra.test.messagingService.nonGracefulShutdown=true
|
||||||
|
-Dcassandra.test.simulator.determinismcheck=strict
|
||||||
|
-Dcassandra.test.sstableformatdevelopment=true
|
||||||
-Dcassandra.tolerate_sstable_size=true
|
-Dcassandra.tolerate_sstable_size=true
|
||||||
-Dcassandra.use_nix_recursive_delete=true
|
-Dcassandra.use_nix_recursive_delete=true
|
||||||
-Dinvalid-legacy-sstable-root=$PROJECT_DIR$/test/data/invalid-legacy-sstables
|
-Dinvalid-legacy-sstable-root=$PROJECT_DIR$/test/data/invalid-legacy-sstables
|
||||||
|
-Djava.library.path=$PROJECT_DIR$/lib/sigar-bin
|
||||||
|
-Djava.security.egd=file:/dev/urandom
|
||||||
-Dlegacy-sstable-root=$PROJECT_DIR$/test/data/legacy-sstables
|
-Dlegacy-sstable-root=$PROJECT_DIR$/test/data/legacy-sstables
|
||||||
-Dlogback.configurationFile=file://$PROJECT_DIR$/test/conf/logback-test.xml
|
-Dlogback.configurationFile=file://$PROJECT_DIR$/test/conf/logback-test.xml
|
||||||
-Dmigration-sstable-root=$PROJECT_DIR$/test/data/migration-sstables
|
-Dmigration-sstable-root=$PROJECT_DIR$/test/data/migration-sstables
|
||||||
-XX:ActiveProcessorCount=2
|
-javaagent:$PROJECT_DIR$/build/test/lib/jars/simulator-asm.jar
|
||||||
|
-Xbootclasspath/a:$PROJECT_DIR$/build/test/lib/jars/simulator-bootstrap.jar
|
||||||
|
-Xmx8G
|
||||||
|
-Xss384k
|
||||||
|
-XX:-BackgroundCompilation
|
||||||
|
-XX:-TieredCompilation
|
||||||
|
-XX:ActiveProcessorCount=4
|
||||||
|
-XX:CICompilerCount=1
|
||||||
-XX:HeapDumpPath=build/test
|
-XX:HeapDumpPath=build/test
|
||||||
-XX:MaxMetaspaceSize=2G
|
-XX:MaxMetaspaceSize=2G
|
||||||
-XX:SoftRefLRUPolicyMSPerMB=0
|
|
||||||
-Xmx4G
|
-Xmx4G
|
||||||
|
-XX:ReservedCodeCacheSize=256M
|
||||||
|
-XX:Tier4CompileThreshold=1000
|
||||||
-ea" />
|
-ea" />
|
||||||
<option name="PARAMETERS" value="" />
|
<option name="PARAMETERS" value="" />
|
||||||
<fork_mode value="class" />
|
<fork_mode value="class" />
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit b9025e59395f47535e4ed1fec20b1186cdb07db8
|
||||||
|
|
@ -25,10 +25,10 @@ from cqlshlib import pylexotron, util
|
||||||
Hint = pylexotron.Hint
|
Hint = pylexotron.Hint
|
||||||
|
|
||||||
cql_keywords_reserved = {'add', 'allow', 'alter', 'and', 'apply', 'asc', 'authorize', 'batch', 'begin', 'by',
|
cql_keywords_reserved = {'add', 'allow', 'alter', 'and', 'apply', 'asc', 'authorize', 'batch', 'begin', 'by',
|
||||||
'columnfamily', 'create', 'delete', 'desc', 'describe', 'drop', 'entries', 'execute', 'from',
|
'columnfamily', 'create', 'commit', 'delete', 'desc', 'describe', 'drop', 'end', 'entries', 'execute', 'from',
|
||||||
'full', 'grant', 'if', 'in', 'index', 'infinity', 'insert', 'into', 'is', 'keyspace', 'limit',
|
'full', 'grant', 'if', 'in', 'index', 'infinity', 'insert', 'into', 'is', 'keyspace', 'let', 'limit',
|
||||||
'materialized', 'modify', 'nan', 'norecursive', 'not', 'null', 'of', 'on', 'or', 'order',
|
'materialized', 'modify', 'nan', 'norecursive', 'not', 'null', 'of', 'on', 'or', 'order',
|
||||||
'primary', 'rename', 'revoke', 'schema', 'select', 'set', 'table', 'to', 'token', 'truncate',
|
'primary', 'rename', 'revoke', 'schema', 'select', 'set', 'table', 'then', 'to', 'token', 'transaction', 'truncate',
|
||||||
'unlogged', 'update', 'use', 'using', 'view', 'where', 'with'}
|
'unlogged', 'update', 'use', 'using', 'view', 'where', 'with'}
|
||||||
"""
|
"""
|
||||||
Set of reserved keywords in CQL.
|
Set of reserved keywords in CQL.
|
||||||
|
|
@ -147,7 +147,7 @@ class CqlParsingRuleSet(pylexotron.ParsingRuleSet):
|
||||||
else:
|
else:
|
||||||
output.append(stmt)
|
output.append(stmt)
|
||||||
if len(stmt) > 2:
|
if len(stmt) > 2:
|
||||||
if stmt[-3][1].upper() == 'APPLY':
|
if stmt[-3][1].upper() == 'APPLY' or stmt[0][1].upper() == 'COMMIT' or (stmt[0][1].upper() == 'END' and stmt[1][1].upper() == 'IF'):
|
||||||
in_batch = False
|
in_batch = False
|
||||||
elif stmt[0][1].upper() == 'BEGIN':
|
elif stmt[0][1].upper() == 'BEGIN':
|
||||||
in_batch = True
|
in_batch = True
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
#ant jar simulator-jars
|
||||||
|
|
||||||
|
DIR=`pwd`
|
||||||
|
JVM_OPTS="$JVM_OPTS -Dcassandra.config=file://$DIR/test/conf/cassandra.yaml"
|
||||||
|
JVM_OPTS="$JVM_OPTS -Dlogback.configurationFile=file://$DIR/test/conf/logback-simulator.xml"
|
||||||
|
JVM_OPTS="$JVM_OPTS -Dcassandra.logdir=$DIR/build/test/logs"
|
||||||
|
#JVM_OPTS="$JVM_OPTS -Djava.library.path=$DIR/lib/sigar-bin"
|
||||||
|
JVM_OPTS="$JVM_OPTS -Dlegacy-sstable-root=$DIR/test/data/legacy-sstables"
|
||||||
|
JVM_OPTS="$JVM_OPTS -Dinvalid-legacy-sstable-root=$DIR/test/data/invalid-legacy-sstables"
|
||||||
|
JVM_OPTS="$JVM_OPTS -Dcassandra.ring_delay_ms=1000"
|
||||||
|
JVM_OPTS="$JVM_OPTS -Dcassandra.skip_sync=true"
|
||||||
|
JVM_OPTS="$JVM_OPTS -ea"
|
||||||
|
JVM_OPTS="$JVM_OPTS -XX:MaxMetaspaceSize=1G"
|
||||||
|
JVM_OPTS="$JVM_OPTS -XX:SoftRefLRUPolicyMSPerMB=0"
|
||||||
|
JVM_OPTS="$JVM_OPTS -Dcassandra.strict.runtime.checks=true"
|
||||||
|
JVM_OPTS="$JVM_OPTS -javaagent:$DIR/build/test/lib/jars/simulator-asm.jar"
|
||||||
|
JVM_OPTS="$JVM_OPTS -Xbootclasspath/a:$DIR/build/test/lib/jars/simulator-bootstrap.jar"
|
||||||
|
JVM_OPTS="$JVM_OPTS -XX:ActiveProcessorCount=4"
|
||||||
|
JVM_OPTS="$JVM_OPTS -XX:-TieredCompilation"
|
||||||
|
JVM_OPTS="$JVM_OPTS -XX:Tier4CompileThreshold=1000"
|
||||||
|
JVM_OPTS="$JVM_OPTS -XX:ReservedCodeCacheSize=256M"
|
||||||
|
JVM_OPTS="$JVM_OPTS -Xmx8G"
|
||||||
|
JVM_OPTS="$JVM_OPTS -Dcassandra.test.simulator.determinismcheck=strict"
|
||||||
|
JVM_OPTS="$JVM_OPTS -Dcassandra.debugrefcount=false"
|
||||||
|
JVM_OPTS="$JVM_OPTS -Dcassandra.skip_sync=true"
|
||||||
|
JVM_OPTS="$JVM_OPTS -Dcassandra.tolerate_sstable_size=true"
|
||||||
|
JVM_OPTS="$JVM_OPTS -Dcassandra.test.simulator.debug=true"
|
||||||
|
JVM_OPTS="$JVM_OPTS -Dcassandra.test.simulator.determinismcheck=strict"
|
||||||
|
echo $JVM_OPTS
|
||||||
|
|
||||||
|
CLASSPATH="$DIR"/build/test/classes
|
||||||
|
for dir in "$DIR"/build/classes/*; do
|
||||||
|
CLASSPATH="$CLASSPATH:$dir"
|
||||||
|
done
|
||||||
|
|
||||||
|
for jar in "$DIR"/lib/*.jar; do
|
||||||
|
CLASSPATH="$CLASSPATH:$jar"
|
||||||
|
done
|
||||||
|
for jar in "$DIR"/build/*.jar; do
|
||||||
|
if [[ $jar != *"logback-classic"* ]]; then
|
||||||
|
CLASSPATH="$CLASSPATH:$jar"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
for jar in "$DIR"/build/lib/jars/*.jar; do
|
||||||
|
if [[ $jar != *"logback-classic"* ]]; then
|
||||||
|
CLASSPATH="$CLASSPATH:$jar"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
for jar in "$DIR"/build/test/lib/jars/*.jar; do
|
||||||
|
if [[ $jar != *"logback-classic"* ]]; then
|
||||||
|
CLASSPATH="$CLASSPATH:$jar"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
CLASS="org.apache.cassandra.simulator.paxos.AccordSimulationRunner"
|
||||||
|
OPTS="run -n 3..6 -t 1000 --cluster-action-limit -1 -c 2 -s 30"
|
||||||
|
|
||||||
|
echo "java -cp <...> $CLASS $OPTS $@"
|
||||||
|
|
||||||
|
while true
|
||||||
|
do
|
||||||
|
echo ""
|
||||||
|
java -cp $CLASSPATH $JVM_OPTS $CLASS $OPTS $@
|
||||||
|
status=$?
|
||||||
|
if [ $status -ne 0 ] ; then
|
||||||
|
exit $status
|
||||||
|
fi
|
||||||
|
|
||||||
|
done
|
||||||
|
|
@ -46,6 +46,7 @@ import Parser,Lexer;
|
||||||
import org.apache.cassandra.cql3.statements.*;
|
import org.apache.cassandra.cql3.statements.*;
|
||||||
import org.apache.cassandra.cql3.statements.schema.*;
|
import org.apache.cassandra.cql3.statements.schema.*;
|
||||||
import org.apache.cassandra.cql3.terms.*;
|
import org.apache.cassandra.cql3.terms.*;
|
||||||
|
import org.apache.cassandra.cql3.transactions.*;
|
||||||
import org.apache.cassandra.exceptions.ConfigurationException;
|
import org.apache.cassandra.exceptions.ConfigurationException;
|
||||||
import org.apache.cassandra.exceptions.InvalidRequestException;
|
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||||
import org.apache.cassandra.exceptions.SyntaxException;
|
import org.apache.cassandra.exceptions.SyntaxException;
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,7 @@ lexer grammar Lexer;
|
||||||
// pylib/cqlshlib/cqlhandling.py::cql_keywords_reserved.
|
// pylib/cqlshlib/cqlhandling.py::cql_keywords_reserved.
|
||||||
// When adding a new unreserved keyword, add entry to unreserved keywords in Parser.g.
|
// When adding a new unreserved keyword, add entry to unreserved keywords in Parser.g.
|
||||||
K_SELECT: S E L E C T;
|
K_SELECT: S E L E C T;
|
||||||
|
K_LET: L E T;
|
||||||
K_FROM: F R O M;
|
K_FROM: F R O M;
|
||||||
K_AS: A S;
|
K_AS: A S;
|
||||||
K_WHERE: W H E R E;
|
K_WHERE: W H E R E;
|
||||||
|
|
@ -83,8 +84,10 @@ K_BEGIN: B E G I N;
|
||||||
K_UNLOGGED: U N L O G G E D;
|
K_UNLOGGED: U N L O G G E D;
|
||||||
K_BATCH: B A T C H;
|
K_BATCH: B A T C H;
|
||||||
K_APPLY: A P P L Y;
|
K_APPLY: A P P L Y;
|
||||||
|
K_COMMIT: C O M M I T;
|
||||||
K_TRUNCATE: T R U N C A T E;
|
K_TRUNCATE: T R U N C A T E;
|
||||||
K_DELETE: D E L E T E;
|
K_DELETE: D E L E T E;
|
||||||
|
K_TRANSACTION: T R A N S A C T I O N;
|
||||||
K_IN: I N;
|
K_IN: I N;
|
||||||
K_CREATE: C R E A T E;
|
K_CREATE: C R E A T E;
|
||||||
K_SCHEMA: S C H E M A;
|
K_SCHEMA: S C H E M A;
|
||||||
|
|
@ -123,6 +126,8 @@ K_DESC: D E S C;
|
||||||
K_ALLOW: A L L O W;
|
K_ALLOW: A L L O W;
|
||||||
K_FILTERING: F I L T E R I N G;
|
K_FILTERING: F I L T E R I N G;
|
||||||
K_IF: I F;
|
K_IF: I F;
|
||||||
|
K_THEN: T H E N;
|
||||||
|
K_END: E N D;
|
||||||
K_IS: I S;
|
K_IS: I S;
|
||||||
K_CONTAINS: C O N T A I N S;
|
K_CONTAINS: C O N T A I N S;
|
||||||
K_BETWEEN: B E T W E E N;
|
K_BETWEEN: B E T W E E N;
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,13 @@ options {
|
||||||
private final List<ErrorListener> listeners = new ArrayList<ErrorListener>();
|
private final List<ErrorListener> listeners = new ArrayList<ErrorListener>();
|
||||||
protected final List<ColumnIdentifier> bindVariables = new ArrayList<ColumnIdentifier>();
|
protected final List<ColumnIdentifier> bindVariables = new ArrayList<ColumnIdentifier>();
|
||||||
|
|
||||||
|
// enables parsing txn specific syntax when true
|
||||||
|
protected boolean isParsingTxn = false;
|
||||||
|
// tracks whether a txn has conditional updates
|
||||||
|
protected boolean isTxnConditional = false;
|
||||||
|
|
||||||
|
protected List<RowDataReference.Raw> references;
|
||||||
|
|
||||||
public static final Set<String> reservedTypeNames = new HashSet<String>()
|
public static final Set<String> reservedTypeNames = new HashSet<String>()
|
||||||
{{
|
{{
|
||||||
add("byte");
|
add("byte");
|
||||||
|
|
@ -59,6 +66,19 @@ options {
|
||||||
return marker;
|
return marker;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RowDataReference.Raw newRowDataReference(Selectable.RawIdentifier tuple, Selectable.Raw selectable)
|
||||||
|
{
|
||||||
|
if (!isParsingTxn)
|
||||||
|
throw new SyntaxException("Cannot create a row data reference unless parsing a transaction");
|
||||||
|
|
||||||
|
if (references == null)
|
||||||
|
references = new ArrayList<>();
|
||||||
|
|
||||||
|
RowDataReference.Raw reference = RowDataReference.Raw.fromSelectable(tuple, selectable);
|
||||||
|
references.add(reference);
|
||||||
|
return reference;
|
||||||
|
}
|
||||||
|
|
||||||
public void addErrorListener(ErrorListener listener)
|
public void addErrorListener(ErrorListener listener)
|
||||||
{
|
{
|
||||||
this.listeners.add(listener);
|
this.listeners.add(listener);
|
||||||
|
|
@ -122,14 +142,24 @@ options {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addRawUpdate(List<Pair<ColumnIdentifier, Operation.RawUpdate>> operations, ColumnIdentifier key, Operation.RawUpdate update)
|
public void addRawUpdate(UpdateStatement.OperationCollector collector, ColumnIdentifier key, Operation.RawUpdate update)
|
||||||
{
|
{
|
||||||
for (Pair<ColumnIdentifier, Operation.RawUpdate> p : operations)
|
if (collector.conflictsWithExistingUpdate(key, update))
|
||||||
{
|
addRecognitionError("Multiple incompatible setting of column " + key);
|
||||||
if (p.left.equals(key) && !p.right.isCompatibleWith(update))
|
if (collector.conflictsWithExistingSubstitution(key))
|
||||||
addRecognitionError("Multiple incompatible setting of column " + key);
|
addRecognitionError("Normal and reference operations for " + key);
|
||||||
}
|
|
||||||
operations.add(Pair.create(key, update));
|
collector.addRawUpdate(key, update);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addRawReferenceOperation(UpdateStatement.OperationCollector collector, ColumnIdentifier key, ReferenceOperation.Raw update)
|
||||||
|
{
|
||||||
|
if (collector.conflictsWithExistingUpdate(key))
|
||||||
|
addRecognitionError("Multiple incompatible setting of column " + key);
|
||||||
|
if (collector.conflictsWithExistingSubstitution(key))
|
||||||
|
addRecognitionError("Normal and reference operations for " + key);
|
||||||
|
|
||||||
|
collector.addRawReferenceOperation(key, update);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<Permission> filterPermissions(Set<Permission> permissions, IResource resource)
|
public Set<Permission> filterPermissions(Set<Permission> permissions, IResource resource)
|
||||||
|
|
@ -237,6 +267,8 @@ cqlStatement returns [CQLStatement.Raw stmt]
|
||||||
| st43=dropIdentityStatement { $stmt = st43; }
|
| st43=dropIdentityStatement { $stmt = st43; }
|
||||||
| st44=listSuperUsersStatement { $stmt = st44; }
|
| st44=listSuperUsersStatement { $stmt = st44; }
|
||||||
| st45=copyTableStatement { $stmt = st45; }
|
| st45=copyTableStatement { $stmt = st45; }
|
||||||
|
| st46=batchTxnStatement { $stmt = st46; }
|
||||||
|
| st47=letStatement { $stmt = st47; }
|
||||||
;
|
;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -276,11 +308,39 @@ selectStatement returns [SelectStatement.RawStatement expr]
|
||||||
groups,
|
groups,
|
||||||
$sclause.isDistinct,
|
$sclause.isDistinct,
|
||||||
allowFiltering,
|
allowFiltering,
|
||||||
isJson);
|
isJson,
|
||||||
|
null);
|
||||||
WhereClause where = wclause == null ? WhereClause.empty() : wclause.build();
|
WhereClause where = wclause == null ? WhereClause.empty() : wclause.build();
|
||||||
$expr = new SelectStatement.RawStatement(cf, params, $sclause.selectors, where, limit, perPartitionLimit);
|
$expr = new SelectStatement.RawStatement(cf, params, $sclause.selectors, where, limit, perPartitionLimit);
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ex. LET x = (SELECT * FROM <table> WHERE k=1 AND c=2)
|
||||||
|
* ex. LET y = (SELECT * FROM <table> WHERE k=1 LIMIT 1)
|
||||||
|
*/
|
||||||
|
letStatement returns [SelectStatement.RawStatement expr]
|
||||||
|
@init {
|
||||||
|
Term.Raw limit = null;
|
||||||
|
}
|
||||||
|
: K_LET txnVar=IDENT '='
|
||||||
|
'(' K_SELECT assignments=letSelectors K_FROM cf=columnFamilyName K_WHERE wclause=whereClause ( K_LIMIT rows=intValue { limit = rows; } )? ')'
|
||||||
|
{
|
||||||
|
SelectStatement.Parameters params = new SelectStatement.Parameters(Collections.emptyList(), Collections.emptyList(), false, false, false, $txnVar.text);
|
||||||
|
WhereClause where = wclause == null ? WhereClause.empty() : wclause.build();
|
||||||
|
$expr = new SelectStatement.RawStatement(cf, params, assignments, where, limit, null);
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
letSelectors returns [List<RawSelector> expr]
|
||||||
|
: t1=letSelector { $expr = new ArrayList<RawSelector>(); $expr.add(t1); } (',' tN=letSelector { $expr.add(tN); })*
|
||||||
|
| '\*' { $expr = Collections.<RawSelector>emptyList();}
|
||||||
|
;
|
||||||
|
|
||||||
|
letSelector returns [RawSelector s]
|
||||||
|
@init{ ColumnIdentifier alias = null; }
|
||||||
|
: us=unaliasedSelector { $s = new RawSelector(us, alias); }
|
||||||
|
;
|
||||||
|
|
||||||
selectClause returns [boolean isDistinct, List<RawSelector> selectors]
|
selectClause returns [boolean isDistinct, List<RawSelector> selectors]
|
||||||
@init{ $isDistinct = false; }
|
@init{ $isDistinct = false; }
|
||||||
|
|
@ -489,7 +549,7 @@ normalInsertStatement [QualifiedName qn] returns [UpdateStatement.ParsedInsert e
|
||||||
}
|
}
|
||||||
: '(' c1=cident { columnNames.add(c1); } ( ',' cn=cident { columnNames.add(cn); } )* ')'
|
: '(' c1=cident { columnNames.add(c1); } ( ',' cn=cident { columnNames.add(cn); } )* ')'
|
||||||
K_VALUES
|
K_VALUES
|
||||||
'(' v1=term { values.add(v1); } ( ',' vn=term { values.add(vn); } )* ')'
|
'(' insertValue[values] ( ',' insertValue[values] )* ')'
|
||||||
( K_IF K_NOT K_EXISTS { ifNotExists = true; } )?
|
( K_IF K_NOT K_EXISTS { ifNotExists = true; } )?
|
||||||
( usingClause[attrs] )?
|
( usingClause[attrs] )?
|
||||||
{
|
{
|
||||||
|
|
@ -497,6 +557,11 @@ normalInsertStatement [QualifiedName qn] returns [UpdateStatement.ParsedInsert e
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
insertValue[List<Term.Raw> values]
|
||||||
|
: t=term { values.add(t); }
|
||||||
|
| {isParsingTxn}? dr=rowDataReference { values.add(new ReferenceValue.Substitution.Raw(dr)); }
|
||||||
|
;
|
||||||
|
|
||||||
jsonInsertStatement [QualifiedName qn] returns [UpdateStatement.ParsedInsertJson expr]
|
jsonInsertStatement [QualifiedName qn] returns [UpdateStatement.ParsedInsertJson expr]
|
||||||
@init {
|
@init {
|
||||||
Attributes.Raw attrs = new Attributes.Raw();
|
Attributes.Raw attrs = new Attributes.Raw();
|
||||||
|
|
@ -537,7 +602,7 @@ usingClauseObjective[Attributes.Raw attrs]
|
||||||
updateStatement returns [UpdateStatement.ParsedUpdate expr]
|
updateStatement returns [UpdateStatement.ParsedUpdate expr]
|
||||||
@init {
|
@init {
|
||||||
Attributes.Raw attrs = new Attributes.Raw();
|
Attributes.Raw attrs = new Attributes.Raw();
|
||||||
List<Pair<ColumnIdentifier, Operation.RawUpdate>> operations = new ArrayList<>();
|
UpdateStatement.OperationCollector operations = new UpdateStatement.OperationCollector();
|
||||||
boolean ifExists = false;
|
boolean ifExists = false;
|
||||||
}
|
}
|
||||||
: K_UPDATE cf=columnFamilyName
|
: K_UPDATE cf=columnFamilyName
|
||||||
|
|
@ -551,7 +616,8 @@ updateStatement returns [UpdateStatement.ParsedUpdate expr]
|
||||||
operations,
|
operations,
|
||||||
wclause.build(),
|
wclause.build(),
|
||||||
conditions == null ? Collections.<ColumnCondition.Raw>emptyList() : conditions,
|
conditions == null ? Collections.<ColumnCondition.Raw>emptyList() : conditions,
|
||||||
ifExists);
|
ifExists,
|
||||||
|
isParsingTxn);
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
@ -650,6 +716,102 @@ batchStatementObjective returns [ModificationStatement.Parsed statement]
|
||||||
| d=deleteStatement { $statement = d; }
|
| d=deleteStatement { $statement = d; }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ex. conditional update returning pre-update values
|
||||||
|
*
|
||||||
|
* BEGIN TRANSACTION
|
||||||
|
* LET row1 = (SELECT * FROM <table> WHERE k=1 AND c=2);
|
||||||
|
* LET row2 = (SELECT * FROM <table> WHERE k=2 AND c=2);
|
||||||
|
* SELECT row1.v, row2.v;
|
||||||
|
* IF row1.v = 3 AND row2.v = 4 THEN
|
||||||
|
* UPDATE <table> SET v = row1.v + 1 WHERE k = 1 AND c = 2;
|
||||||
|
* END IF
|
||||||
|
* COMMIT TRANSACTION
|
||||||
|
*
|
||||||
|
* ex. read-only transaction
|
||||||
|
*
|
||||||
|
* BEGIN TRANSACTION
|
||||||
|
* SELECT * FROM <table> WHERE k=1 AND c=2;
|
||||||
|
* COMMIT TRANSACTION
|
||||||
|
*
|
||||||
|
* ex. write-only transaction
|
||||||
|
*
|
||||||
|
* BEGIN TRANSACTION
|
||||||
|
* INSERT INTO <table> (k, c, v) VALUES (0, 0, 1);
|
||||||
|
* COMMIT TRANSACTION
|
||||||
|
*/
|
||||||
|
batchTxnStatement returns [TransactionStatement.Parsed expr]
|
||||||
|
@init {
|
||||||
|
isParsingTxn = true;
|
||||||
|
List<SelectStatement.RawStatement> assignments = new ArrayList<>();
|
||||||
|
SelectStatement.RawStatement select = null;
|
||||||
|
List<RowDataReference.Raw> returning = null;
|
||||||
|
List<ModificationStatement.Parsed> updates = new ArrayList<>();
|
||||||
|
}
|
||||||
|
: K_BEGIN K_TRANSACTION
|
||||||
|
( let=letStatement ';' { assignments.add(let); })*
|
||||||
|
( ( (selectStatement) => s=selectStatement ';' { select = s; }) | ( K_SELECT drs=rowDataReferences ';' { returning = drs; }) )?
|
||||||
|
( K_IF conditions=txnConditions K_THEN { isTxnConditional = true; } )?
|
||||||
|
( upd=batchStatementObjective ';' { updates.add(upd); } )*
|
||||||
|
( {!isTxnConditional}? (K_COMMIT K_TRANSACTION) | {isTxnConditional}? (K_END K_IF K_COMMIT K_TRANSACTION))
|
||||||
|
{
|
||||||
|
$expr = new TransactionStatement.Parsed(assignments, select, returning, updates, conditions, references);
|
||||||
|
}
|
||||||
|
;
|
||||||
|
finally { isParsingTxn = false; }
|
||||||
|
|
||||||
|
rowDataReferences returns [List<RowDataReference.Raw> refs]
|
||||||
|
: r1=rowDataReference { refs = new ArrayList<RowDataReference.Raw>(); refs.add(r1); } (',' rN=rowDataReference { refs.add(rN); })*
|
||||||
|
;
|
||||||
|
|
||||||
|
rowDataReference returns [RowDataReference.Raw rawRef]
|
||||||
|
@init { Selectable.RawIdentifier tuple = null; Selectable.Raw selectable = null; }
|
||||||
|
@after { $rawRef = newRowDataReference(tuple, selectable); }
|
||||||
|
: t=sident ('.' s=referenceSelection)? { tuple = t; selectable = s; }
|
||||||
|
;
|
||||||
|
|
||||||
|
referenceSelection returns [Selectable.Raw s]
|
||||||
|
: g=referenceSelectionWithoutField m=selectorModifier[g] {$s = m;}
|
||||||
|
;
|
||||||
|
|
||||||
|
referenceSelectionWithoutField returns [Selectable.Raw s]
|
||||||
|
@init { Selectable.Raw tmp = null; }
|
||||||
|
@after { $s = tmp; }
|
||||||
|
: sn=sident { tmp=sn; }
|
||||||
|
| (selectionTypeHint)=> h=selectionTypeHint { tmp=h; }
|
||||||
|
| t=selectionTupleOrNestedSelector { tmp=t; }
|
||||||
|
| l=selectionList { tmp=l; }
|
||||||
|
| m=selectionMapOrSet { tmp=m; }
|
||||||
|
// UDTs are equivalent to maps from the syntax point of view, so the final decision will be done in Selectable.WithMapOrUdt
|
||||||
|
;
|
||||||
|
|
||||||
|
txnConditions returns [List<ConditionStatement.Raw> conditions]
|
||||||
|
@init { conditions = new ArrayList<ConditionStatement.Raw>(); }
|
||||||
|
: txnColumnCondition[conditions] ( K_AND txnColumnCondition[conditions] )*
|
||||||
|
;
|
||||||
|
|
||||||
|
txnConditionKind returns [ConditionStatement.Kind op]
|
||||||
|
: '=' { $op = ConditionStatement.Kind.EQ; }
|
||||||
|
| '<' { $op = ConditionStatement.Kind.LT; }
|
||||||
|
| '<=' { $op = ConditionStatement.Kind.LTE; }
|
||||||
|
| '>' { $op = ConditionStatement.Kind.GT; }
|
||||||
|
| '>=' { $op = ConditionStatement.Kind.GTE; }
|
||||||
|
| '!=' { $op = ConditionStatement.Kind.NEQ; }
|
||||||
|
;
|
||||||
|
|
||||||
|
txnColumnCondition[List<ConditionStatement.Raw> conditions]
|
||||||
|
: lhs=rowDataReference
|
||||||
|
(
|
||||||
|
K_IS
|
||||||
|
(
|
||||||
|
K_NOT K_NULL { conditions.add(new ConditionStatement.Raw(lhs, ConditionStatement.Kind.IS_NOT_NULL, null)); }
|
||||||
|
| K_NULL { conditions.add(new ConditionStatement.Raw(lhs, ConditionStatement.Kind.IS_NULL, null)); }
|
||||||
|
)
|
||||||
|
| (txnConditionKind term)=> op=txnConditionKind t=term { conditions.add(new ConditionStatement.Raw(lhs, op, t)); }
|
||||||
|
)
|
||||||
|
| lhs=term op=txnConditionKind rhs=rowDataReference { conditions.add(new ConditionStatement.Raw(lhs, op, rhs)); }
|
||||||
|
;
|
||||||
|
|
||||||
createAggregateStatement returns [CreateAggregateStatement.Raw stmt]
|
createAggregateStatement returns [CreateAggregateStatement.Raw stmt]
|
||||||
@init {
|
@init {
|
||||||
boolean orReplace = false;
|
boolean orReplace = false;
|
||||||
|
|
@ -1727,18 +1889,18 @@ simpleTerm returns [Term.Raw term]
|
||||||
| K_CAST '(' t=simpleTerm K_AS n=native_type ')' { $term = FunctionCall.Raw.newCast(t, n); }
|
| K_CAST '(' t=simpleTerm K_AS n=native_type ')' { $term = FunctionCall.Raw.newCast(t, n); }
|
||||||
;
|
;
|
||||||
|
|
||||||
columnOperation[List<Pair<ColumnIdentifier, Operation.RawUpdate>> operations]
|
columnOperation[UpdateStatement.OperationCollector operations]
|
||||||
: key=cident columnOperationDifferentiator[operations, key]
|
: key=cident columnOperationDifferentiator[operations, key]
|
||||||
;
|
;
|
||||||
|
|
||||||
columnOperationDifferentiator[List<Pair<ColumnIdentifier, Operation.RawUpdate>> operations, ColumnIdentifier key]
|
columnOperationDifferentiator[UpdateStatement.OperationCollector operations, ColumnIdentifier key]
|
||||||
: '=' normalColumnOperation[operations, key]
|
: '=' normalColumnOperation[operations, key]
|
||||||
| shorthandColumnOperation[operations, key]
|
| shorthandColumnOperation[operations, key]
|
||||||
| '[' k=term ']' collectionColumnOperation[operations, key, k]
|
| '[' k=term ']' collectionColumnOperation[operations, key, k]
|
||||||
| '.' field=fident udtColumnOperation[operations, key, field]
|
| '.' field=fident udtColumnOperation[operations, key, field]
|
||||||
;
|
;
|
||||||
|
|
||||||
normalColumnOperation[List<Pair<ColumnIdentifier, Operation.RawUpdate>> operations, ColumnIdentifier key]
|
normalColumnOperation[UpdateStatement.OperationCollector operations, ColumnIdentifier key]
|
||||||
: t=term ('+' c=cident )?
|
: t=term ('+' c=cident )?
|
||||||
{
|
{
|
||||||
if (c == null)
|
if (c == null)
|
||||||
|
|
@ -1766,27 +1928,56 @@ normalColumnOperation[List<Pair<ColumnIdentifier, Operation.RawUpdate>> operatio
|
||||||
addRecognitionError("Only expressions of the form X = X " + ($i.text.charAt(0) == '-' ? '-' : '+') + " <value> are supported.");
|
addRecognitionError("Only expressions of the form X = X " + ($i.text.charAt(0) == '-' ? '-' : '+') + " <value> are supported.");
|
||||||
addRawUpdate(operations, key, new Operation.Addition(Constants.Literal.integer($i.text)));
|
addRawUpdate(operations, key, new Operation.Addition(Constants.Literal.integer($i.text)));
|
||||||
}
|
}
|
||||||
|
| {isParsingTxn}? r=rowDataReference
|
||||||
|
{
|
||||||
|
addRawReferenceOperation(operations, key, new ReferenceOperation.Raw(new Operation.SetValue(r), key, new ReferenceValue.Substitution.Raw(r)));
|
||||||
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
shorthandColumnOperation[List<Pair<ColumnIdentifier, Operation.RawUpdate>> operations, ColumnIdentifier key]
|
shorthandColumnOperation[UpdateStatement.OperationCollector operations, ColumnIdentifier key]
|
||||||
: sig=('+=' | '-=') t=term
|
: sig=('+=' | '-=')
|
||||||
{
|
(
|
||||||
addRawUpdate(operations, key, $sig.text.equals("+=") ? new Operation.Addition(t) : new Operation.Substraction(t));
|
t=term
|
||||||
}
|
{
|
||||||
|
addRawUpdate(operations, key, $sig.text.equals("+=") ? new Operation.Addition(t) : new Operation.Substraction(t));
|
||||||
|
}
|
||||||
|
| {isParsingTxn}? dr=rowDataReference
|
||||||
|
{
|
||||||
|
ReferenceValue.Raw right = new ReferenceValue.Substitution.Raw(dr);
|
||||||
|
Operation.RawUpdate operation = $sig.text.equals("+=") ? new Operation.Addition(dr) : new Operation.Substraction(dr);
|
||||||
|
addRawReferenceOperation(operations, key, new ReferenceOperation.Raw(operation, key, right));
|
||||||
|
}
|
||||||
|
)
|
||||||
;
|
;
|
||||||
|
|
||||||
collectionColumnOperation[List<Pair<ColumnIdentifier, Operation.RawUpdate>> operations, ColumnIdentifier key, Term.Raw k]
|
collectionColumnOperation[UpdateStatement.OperationCollector operations, ColumnIdentifier key, Term.Raw k]
|
||||||
: '=' t=term
|
: '='
|
||||||
{
|
(
|
||||||
addRawUpdate(operations, key, new Operation.SetElement(k, t));
|
t=term
|
||||||
}
|
{
|
||||||
|
addRawUpdate(operations, key, new Operation.SetElement(k, t));
|
||||||
|
}
|
||||||
|
| {isParsingTxn}? dr=rowDataReference
|
||||||
|
{
|
||||||
|
ReferenceValue.Raw right = new ReferenceValue.Substitution.Raw(dr);
|
||||||
|
addRawReferenceOperation(operations, key, new ReferenceOperation.Raw(new Operation.SetElement(k, dr), key, right));
|
||||||
|
}
|
||||||
|
)
|
||||||
;
|
;
|
||||||
|
|
||||||
udtColumnOperation[List<Pair<ColumnIdentifier, Operation.RawUpdate>> operations, ColumnIdentifier key, FieldIdentifier field]
|
udtColumnOperation[UpdateStatement.OperationCollector operations, ColumnIdentifier key, FieldIdentifier field]
|
||||||
: '=' t=term
|
: '='
|
||||||
{
|
(
|
||||||
addRawUpdate(operations, key, new Operation.SetField(field, t));
|
t=term
|
||||||
}
|
{
|
||||||
|
addRawUpdate(operations, key, new Operation.SetField(field, t));
|
||||||
|
}
|
||||||
|
| {isParsingTxn}? dr=rowDataReference
|
||||||
|
{
|
||||||
|
ReferenceValue.Raw right = new ReferenceValue.Substitution.Raw(dr);
|
||||||
|
addRawReferenceOperation(operations, key, new ReferenceOperation.Raw(new Operation.SetField(field, dr), key, right));
|
||||||
|
}
|
||||||
|
)
|
||||||
;
|
;
|
||||||
|
|
||||||
columnCondition returns [ColumnCondition.Raw condition]
|
columnCondition returns [ColumnCondition.Raw condition]
|
||||||
|
|
|
||||||
|
|
@ -23,5 +23,5 @@ package org.apache.cassandra.audit;
|
||||||
*/
|
*/
|
||||||
public enum AuditLogEntryCategory
|
public enum AuditLogEntryCategory
|
||||||
{
|
{
|
||||||
QUERY, DML, DDL, DCL, OTHER, AUTH, ERROR, PREPARE, JMX
|
QUERY, DML, DDL, DCL, OTHER, AUTH, ERROR, PREPARE, JMX, TRANSACTION
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@ public enum AuditLogEntryType
|
||||||
DROP_IDENTITY(AuditLogEntryCategory.DCL),
|
DROP_IDENTITY(AuditLogEntryCategory.DCL),
|
||||||
USE_KEYSPACE(AuditLogEntryCategory.OTHER),
|
USE_KEYSPACE(AuditLogEntryCategory.OTHER),
|
||||||
DESCRIBE(AuditLogEntryCategory.OTHER),
|
DESCRIBE(AuditLogEntryCategory.OTHER),
|
||||||
|
TRANSACTION(AuditLogEntryCategory.TRANSACTION),
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Common Audit Log Entry Types
|
* Common Audit Log Entry Types
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ final class AuditLogFilter
|
||||||
{
|
{
|
||||||
private static final Logger logger = LoggerFactory.getLogger(AuditLogFilter.class);
|
private static final Logger logger = LoggerFactory.getLogger(AuditLogFilter.class);
|
||||||
|
|
||||||
private static ImmutableSet<String> EMPTY_FILTERS = ImmutableSet.of();
|
private static final ImmutableSet<String> EMPTY_FILTERS = ImmutableSet.of();
|
||||||
|
|
||||||
final ImmutableSet<String> excludedKeyspaces;
|
final ImmutableSet<String> excludedKeyspaces;
|
||||||
final ImmutableSet<String> includedKeyspaces;
|
final ImmutableSet<String> includedKeyspaces;
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ public class SingleThreadExecutorPlus extends ThreadPoolExecutorPlus implements
|
||||||
{
|
{
|
||||||
public static class AtLeastOnce extends AtomicBoolean implements AtLeastOnceTrigger, Runnable
|
public static class AtLeastOnce extends AtomicBoolean implements AtLeastOnceTrigger, Runnable
|
||||||
{
|
{
|
||||||
|
private static final long serialVersionUID = 0; // for simulator support
|
||||||
protected final SequentialExecutorPlus executor;
|
protected final SequentialExecutorPlus executor;
|
||||||
protected final Runnable run;
|
protected final Runnable run;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ public enum Stage
|
||||||
MUTATION (true, "MutationStage", "request", DatabaseDescriptor::getConcurrentWriters, DatabaseDescriptor::setConcurrentWriters, Stage::multiThreadedLowSignalStage),
|
MUTATION (true, "MutationStage", "request", DatabaseDescriptor::getConcurrentWriters, DatabaseDescriptor::setConcurrentWriters, Stage::multiThreadedLowSignalStage),
|
||||||
COUNTER_MUTATION (true, "CounterMutationStage", "request", DatabaseDescriptor::getConcurrentCounterWriters, DatabaseDescriptor::setConcurrentCounterWriters, Stage::multiThreadedLowSignalStage),
|
COUNTER_MUTATION (true, "CounterMutationStage", "request", DatabaseDescriptor::getConcurrentCounterWriters, DatabaseDescriptor::setConcurrentCounterWriters, Stage::multiThreadedLowSignalStage),
|
||||||
VIEW_MUTATION (true, "ViewMutationStage", "request", DatabaseDescriptor::getConcurrentViewWriters, DatabaseDescriptor::setConcurrentViewWriters, Stage::multiThreadedLowSignalStage),
|
VIEW_MUTATION (true, "ViewMutationStage", "request", DatabaseDescriptor::getConcurrentViewWriters, DatabaseDescriptor::setConcurrentViewWriters, Stage::multiThreadedLowSignalStage),
|
||||||
|
ACCORD (true, "AccordStage", "request", DatabaseDescriptor::getConcurrentAccordOps, DatabaseDescriptor::setConcurrentAccordOps, Stage::multiThreadedLowSignalStage),
|
||||||
GOSSIP (true, "GossipStage", "internal", () -> 1, null, Stage::singleThreadedStage),
|
GOSSIP (true, "GossipStage", "internal", () -> 1, null, Stage::singleThreadedStage),
|
||||||
REQUEST_RESPONSE (false, "RequestResponseStage", "request", FBUtilities::getAvailableProcessors, null, Stage::multiThreadedLowSignalStage),
|
REQUEST_RESPONSE (false, "RequestResponseStage", "request", FBUtilities::getAvailableProcessors, null, Stage::multiThreadedLowSignalStage),
|
||||||
ANTI_ENTROPY (false, "AntiEntropyStage", "internal", () -> 1, null, Stage::singleThreadedStage),
|
ANTI_ENTROPY (false, "AntiEntropyStage", "internal", () -> 1, null, Stage::singleThreadedStage),
|
||||||
|
|
@ -59,7 +60,6 @@ public enum Stage
|
||||||
INTERNAL_METADATA (false, "InternalMetadataStage", "internal", FBUtilities::getAvailableProcessors, null, Stage::multiThreadedStage),
|
INTERNAL_METADATA (false, "InternalMetadataStage", "internal", FBUtilities::getAvailableProcessors, null, Stage::multiThreadedStage),
|
||||||
FETCH_LOG (false, "MetadataFetchLogStage", "internal", () -> 1, null, Stage::singleThreadedStage)
|
FETCH_LOG (false, "MetadataFetchLogStage", "internal", () -> 1, null, Stage::singleThreadedStage)
|
||||||
;
|
;
|
||||||
|
|
||||||
public final String jmxName;
|
public final String jmxName;
|
||||||
private final Supplier<ExecutorPlus> executorSupplier;
|
private final Supplier<ExecutorPlus> executorSupplier;
|
||||||
private volatile ExecutorPlus executor;
|
private volatile ExecutorPlus executor;
|
||||||
|
|
|
||||||
|
|
@ -491,6 +491,8 @@ public enum CassandraRelevantProperties
|
||||||
SERIALIZATION_EMPTY_TYPE_NONEMPTY_BEHAVIOR("cassandra.serialization.emptytype.nonempty_behavior"),
|
SERIALIZATION_EMPTY_TYPE_NONEMPTY_BEHAVIOR("cassandra.serialization.emptytype.nonempty_behavior"),
|
||||||
SET_SEP_THREAD_NAME("cassandra.set_sep_thread_name", "true"),
|
SET_SEP_THREAD_NAME("cassandra.set_sep_thread_name", "true"),
|
||||||
SHUTDOWN_ANNOUNCE_DELAY_IN_MS("cassandra.shutdown_announce_in_ms", "2000"),
|
SHUTDOWN_ANNOUNCE_DELAY_IN_MS("cassandra.shutdown_announce_in_ms", "2000"),
|
||||||
|
SIMULATOR_SEED("cassandra.simulator.seed"),
|
||||||
|
SIMULATOR_STARTED("cassandra.simulator.started"),
|
||||||
SIZE_RECORDER_INTERVAL("cassandra.size_recorder_interval", "300"),
|
SIZE_RECORDER_INTERVAL("cassandra.size_recorder_interval", "300"),
|
||||||
SKIP_AUTH_SETUP("cassandra.skip_auth_setup", "false"),
|
SKIP_AUTH_SETUP("cassandra.skip_auth_setup", "false"),
|
||||||
SKIP_GC_INSPECTOR("cassandra.skip_gc_inspector", "false"),
|
SKIP_GC_INSPECTOR("cassandra.skip_gc_inspector", "false"),
|
||||||
|
|
@ -595,6 +597,7 @@ public enum CassandraRelevantProperties
|
||||||
TEST_READ_ITERATION_DELAY_MS("cassandra.test.read_iteration_delay_ms", "0"),
|
TEST_READ_ITERATION_DELAY_MS("cassandra.test.read_iteration_delay_ms", "0"),
|
||||||
TEST_REUSE_PREPARED("cassandra.test.reuse_prepared", "true"),
|
TEST_REUSE_PREPARED("cassandra.test.reuse_prepared", "true"),
|
||||||
TEST_ROW_CACHE_SIZE("cassandra.test.row_cache_size"),
|
TEST_ROW_CACHE_SIZE("cassandra.test.row_cache_size"),
|
||||||
|
TEST_SEED("cassandra.test.seed"),
|
||||||
TEST_SERIALIZATION_WRITES("cassandra.test-serialization-writes"),
|
TEST_SERIALIZATION_WRITES("cassandra.test-serialization-writes"),
|
||||||
TEST_SIMULATOR_DEBUG("cassandra.test.simulator.debug"),
|
TEST_SIMULATOR_DEBUG("cassandra.test.simulator.debug"),
|
||||||
TEST_SIMULATOR_DETERMINISM_CHECK("cassandra.test.simulator.determinismcheck", "none"),
|
TEST_SIMULATOR_DETERMINISM_CHECK("cassandra.test.simulator.determinismcheck", "none"),
|
||||||
|
|
|
||||||
|
|
@ -174,6 +174,8 @@ public class Config
|
||||||
|
|
||||||
public volatile DurationSpec.LongMillisecondsBound stream_transfer_task_timeout = new DurationSpec.LongMillisecondsBound("12h");
|
public volatile DurationSpec.LongMillisecondsBound stream_transfer_task_timeout = new DurationSpec.LongMillisecondsBound("12h");
|
||||||
|
|
||||||
|
public volatile DurationSpec.LongMillisecondsBound transaction_timeout = new DurationSpec.LongMillisecondsBound("30s");
|
||||||
|
|
||||||
public volatile DurationSpec.LongMillisecondsBound cms_await_timeout = new DurationSpec.LongMillisecondsBound("120000ms");
|
public volatile DurationSpec.LongMillisecondsBound cms_await_timeout = new DurationSpec.LongMillisecondsBound("120000ms");
|
||||||
public volatile int cms_default_max_retries = 10;
|
public volatile int cms_default_max_retries = 10;
|
||||||
public volatile DurationSpec.IntMillisecondsBound cms_default_retry_backoff = new DurationSpec.IntMillisecondsBound("50ms");
|
public volatile DurationSpec.IntMillisecondsBound cms_default_retry_backoff = new DurationSpec.IntMillisecondsBound("50ms");
|
||||||
|
|
@ -188,6 +190,7 @@ public class Config
|
||||||
|
|
||||||
public int concurrent_reads = 32;
|
public int concurrent_reads = 32;
|
||||||
public int concurrent_writes = 32;
|
public int concurrent_writes = 32;
|
||||||
|
public int concurrent_accord_operations = 32;
|
||||||
public int concurrent_counter_writes = 32;
|
public int concurrent_counter_writes = 32;
|
||||||
public int concurrent_materialized_view_writes = 32;
|
public int concurrent_materialized_view_writes = 32;
|
||||||
public int available_processors = -1;
|
public int available_processors = -1;
|
||||||
|
|
@ -620,6 +623,8 @@ public class Config
|
||||||
|
|
||||||
public volatile boolean use_statements_enabled = true;
|
public volatile boolean use_statements_enabled = true;
|
||||||
|
|
||||||
|
public boolean accord_transactions_enabled = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optionally disable asynchronous UDF execution.
|
* Optionally disable asynchronous UDF execution.
|
||||||
* Disabling asynchronous UDF execution also implicitly disables the security-manager!
|
* Disabling asynchronous UDF execution also implicitly disables the security-manager!
|
||||||
|
|
@ -1159,6 +1164,8 @@ public class Config
|
||||||
|
|
||||||
public volatile boolean client_request_size_metrics_enabled = true;
|
public volatile boolean client_request_size_metrics_enabled = true;
|
||||||
|
|
||||||
|
public LegacyPaxosStrategy legacy_paxos_strategy = LegacyPaxosStrategy.migration;
|
||||||
|
|
||||||
public volatile int max_top_size_partition_count = 10;
|
public volatile int max_top_size_partition_count = 10;
|
||||||
public volatile int max_top_tombstone_partition_count = 10;
|
public volatile int max_top_tombstone_partition_count = 10;
|
||||||
public volatile DataStorageSpec.LongBytesBound min_tracked_partition_size = new DataStorageSpec.LongBytesBound("1MiB");
|
public volatile DataStorageSpec.LongBytesBound min_tracked_partition_size = new DataStorageSpec.LongBytesBound("1MiB");
|
||||||
|
|
@ -1374,6 +1381,29 @@ public class Config
|
||||||
cell
|
cell
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How to pick a consensus protocol for CAS
|
||||||
|
* and serial read operations. Transaction statements
|
||||||
|
* will always run on Accord. Legacy in this context includes PaxosV2.
|
||||||
|
*/
|
||||||
|
public enum LegacyPaxosStrategy
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Allow both Accord and PaxosV1/V2 to run on the same cluster
|
||||||
|
* Some keys and ranges might be running on Accord if they
|
||||||
|
* have been migrated and the rest will run on Paxos until
|
||||||
|
* they are migrated.
|
||||||
|
*/
|
||||||
|
migration,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Everything will be run on Accord. Useful for new deployments
|
||||||
|
* that don't want to accidentally start using legacy Paxos
|
||||||
|
* requiring migration to Accord.
|
||||||
|
*/
|
||||||
|
accord
|
||||||
|
}
|
||||||
|
|
||||||
private static final Set<String> SENSITIVE_KEYS = new HashSet<String>() {{
|
private static final Set<String> SENSITIVE_KEYS = new HashSet<String>() {{
|
||||||
add("client_encryption_options");
|
add("client_encryption_options");
|
||||||
add("server_encryption_options");
|
add("server_encryption_options");
|
||||||
|
|
@ -1400,10 +1430,10 @@ public class Config
|
||||||
String value;
|
String value;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Field.get() can throw NPE if the value of the field is null
|
Object obj = field.get(config);
|
||||||
value = field.get(config).toString();
|
value = obj != null ? obj.toString() : "null";
|
||||||
}
|
}
|
||||||
catch (NullPointerException | IllegalAccessException npe)
|
catch (IllegalAccessException npe)
|
||||||
{
|
{
|
||||||
value = "null";
|
value = "null";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -166,6 +166,9 @@ import static org.apache.cassandra.utils.Clock.Global.logInitializationOutcome;
|
||||||
|
|
||||||
public class DatabaseDescriptor
|
public class DatabaseDescriptor
|
||||||
{
|
{
|
||||||
|
public static final String NO_ACCORD_PAXOS_STRATEGY_WITH_ACCORD_DISABLED_MESSAGE =
|
||||||
|
"Cannot use legacy_paxos_strategy \"accord\" while Accord transactions are disabled.";
|
||||||
|
|
||||||
static
|
static
|
||||||
{
|
{
|
||||||
CHRONICLE_ANALYTICS_DISABLE.setBoolean(true);
|
CHRONICLE_ANALYTICS_DISABLE.setBoolean(true);
|
||||||
|
|
@ -640,6 +643,9 @@ public class DatabaseDescriptor
|
||||||
if (conf.concurrent_counter_writes < 2)
|
if (conf.concurrent_counter_writes < 2)
|
||||||
throw new ConfigurationException("concurrent_counter_writes must be at least 2, but was " + conf.concurrent_counter_writes, false);
|
throw new ConfigurationException("concurrent_counter_writes must be at least 2, but was " + conf.concurrent_counter_writes, false);
|
||||||
|
|
||||||
|
if (conf.concurrent_accord_operations < 1)
|
||||||
|
throw new ConfigurationException("concurrent_accord_operations must be at least 1, but was " + conf.concurrent_accord_operations, false);
|
||||||
|
|
||||||
if (conf.networking_cache_size == null)
|
if (conf.networking_cache_size == null)
|
||||||
conf.networking_cache_size = new DataStorageSpec.IntMebibytesBound(Math.min(128, (int) (Runtime.getRuntime().maxMemory() / (16 * 1048576))));
|
conf.networking_cache_size = new DataStorageSpec.IntMebibytesBound(Math.min(128, (int) (Runtime.getRuntime().maxMemory() / (16 * 1048576))));
|
||||||
|
|
||||||
|
|
@ -1116,6 +1122,9 @@ public class DatabaseDescriptor
|
||||||
// run audit logging options through sanitation and validation
|
// run audit logging options through sanitation and validation
|
||||||
if (conf.audit_logging_options != null)
|
if (conf.audit_logging_options != null)
|
||||||
setAuditLoggingOptions(conf.audit_logging_options);
|
setAuditLoggingOptions(conf.audit_logging_options);
|
||||||
|
|
||||||
|
if (conf.legacy_paxos_strategy == Config.LegacyPaxosStrategy.accord && !conf.accord_transactions_enabled)
|
||||||
|
throw new ConfigurationException(NO_ACCORD_PAXOS_STRATEGY_WITH_ACCORD_DISABLED_MESSAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
|
|
@ -1473,6 +1482,12 @@ public class DatabaseDescriptor
|
||||||
logInfo("truncate_request_timeout", conf.truncate_request_timeout, LOWEST_ACCEPTED_TIMEOUT);
|
logInfo("truncate_request_timeout", conf.truncate_request_timeout, LOWEST_ACCEPTED_TIMEOUT);
|
||||||
conf.truncate_request_timeout = LOWEST_ACCEPTED_TIMEOUT;
|
conf.truncate_request_timeout = LOWEST_ACCEPTED_TIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (conf.transaction_timeout.toMilliseconds() < LOWEST_ACCEPTED_TIMEOUT.toMilliseconds())
|
||||||
|
{
|
||||||
|
logInfo("transaction_timeout", conf.transaction_timeout, LOWEST_ACCEPTED_TIMEOUT);
|
||||||
|
conf.transaction_timeout = LOWEST_ACCEPTED_TIMEOUT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void logInfo(String property, DurationSpec.LongMillisecondsBound actualValue, DurationSpec.LongMillisecondsBound lowestAcceptedValue)
|
private static void logInfo(String property, DurationSpec.LongMillisecondsBound actualValue, DurationSpec.LongMillisecondsBound lowestAcceptedValue)
|
||||||
|
|
@ -2427,6 +2442,16 @@ public class DatabaseDescriptor
|
||||||
return conf.cas_contention_timeout.to(unit);
|
return conf.cas_contention_timeout.to(unit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static long getTransactionTimeout(TimeUnit unit)
|
||||||
|
{
|
||||||
|
return conf.transaction_timeout.to(unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setTransactionTimeout(long timeOutInMillis)
|
||||||
|
{
|
||||||
|
conf.transaction_timeout = new DurationSpec.LongMillisecondsBound(timeOutInMillis);
|
||||||
|
}
|
||||||
|
|
||||||
public static void setCasContentionTimeout(long timeOutInMillis)
|
public static void setCasContentionTimeout(long timeOutInMillis)
|
||||||
{
|
{
|
||||||
conf.cas_contention_timeout = new DurationSpec.LongMillisecondsBound(timeOutInMillis);
|
conf.cas_contention_timeout = new DurationSpec.LongMillisecondsBound(timeOutInMillis);
|
||||||
|
|
@ -2645,6 +2670,20 @@ public class DatabaseDescriptor
|
||||||
conf.concurrent_materialized_view_writes = concurrent_materialized_view_writes;
|
conf.concurrent_materialized_view_writes = concurrent_materialized_view_writes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int getConcurrentAccordOps()
|
||||||
|
{
|
||||||
|
return conf.concurrent_accord_operations;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setConcurrentAccordOps(int concurrent_operations)
|
||||||
|
{
|
||||||
|
if (concurrent_operations < 0)
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("Concurrent accord operations must be non-negative");
|
||||||
|
}
|
||||||
|
conf.concurrent_accord_operations = concurrent_operations;
|
||||||
|
}
|
||||||
|
|
||||||
public static int getFlushWriters()
|
public static int getFlushWriters()
|
||||||
{
|
{
|
||||||
return conf.memtable_flush_writers;
|
return conf.memtable_flush_writers;
|
||||||
|
|
@ -3545,6 +3584,11 @@ public class DatabaseDescriptor
|
||||||
return conf.paxos_topology_repair_strict_each_quorum;
|
return conf.paxos_topology_repair_strict_each_quorum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Config.LegacyPaxosStrategy getLegacyPaxosStrategy()
|
||||||
|
{
|
||||||
|
return conf.legacy_paxos_strategy;
|
||||||
|
}
|
||||||
|
|
||||||
public static void setNativeTransportMaxRequestDataInFlightPerIpInBytes(long maxRequestDataInFlightInBytes)
|
public static void setNativeTransportMaxRequestDataInFlightPerIpInBytes(long maxRequestDataInFlightInBytes)
|
||||||
{
|
{
|
||||||
if (maxRequestDataInFlightInBytes == -1)
|
if (maxRequestDataInFlightInBytes == -1)
|
||||||
|
|
@ -5144,6 +5188,16 @@ public class DatabaseDescriptor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean getAccordTransactionsEnabled()
|
||||||
|
{
|
||||||
|
return conf.accord_transactions_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setAccordTransactionsEnabled(boolean b)
|
||||||
|
{
|
||||||
|
conf.accord_transactions_enabled = b;
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean getForceNewPreparedStatementBehaviour()
|
public static boolean getForceNewPreparedStatementBehaviour()
|
||||||
{
|
{
|
||||||
return conf.force_new_prepared_statement_behaviour;
|
return conf.force_new_prepared_statement_behaviour;
|
||||||
|
|
|
||||||
|
|
@ -133,4 +133,14 @@ public interface CQLStatement
|
||||||
{
|
{
|
||||||
String keyspace();
|
String keyspace();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface CompositeCQLStatement extends CQLStatement
|
||||||
|
{
|
||||||
|
Iterable<? extends CQLStatement> getStatements();
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ReturningCQLStatement extends CQLStatement
|
||||||
|
{
|
||||||
|
ResultSet.ResultMetadata getResultMetadata();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,15 @@ import org.apache.cassandra.cql3.terms.Sets;
|
||||||
import org.apache.cassandra.cql3.terms.Term;
|
import org.apache.cassandra.cql3.terms.Term;
|
||||||
import org.apache.cassandra.cql3.terms.UserTypes;
|
import org.apache.cassandra.cql3.terms.UserTypes;
|
||||||
import org.apache.cassandra.db.DecoratedKey;
|
import org.apache.cassandra.db.DecoratedKey;
|
||||||
import org.apache.cassandra.db.marshal.*;
|
import org.apache.cassandra.db.marshal.CollectionType;
|
||||||
|
import org.apache.cassandra.db.marshal.CounterColumnType;
|
||||||
|
import org.apache.cassandra.db.marshal.ListType;
|
||||||
|
import org.apache.cassandra.db.marshal.MapType;
|
||||||
|
import org.apache.cassandra.db.marshal.NumberType;
|
||||||
|
import org.apache.cassandra.db.marshal.SetType;
|
||||||
|
import org.apache.cassandra.db.marshal.StringType;
|
||||||
|
import org.apache.cassandra.db.marshal.TupleType;
|
||||||
|
import org.apache.cassandra.db.marshal.UserType;
|
||||||
import org.apache.cassandra.exceptions.InvalidRequestException;
|
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||||
import org.apache.cassandra.schema.ColumnMetadata;
|
import org.apache.cassandra.schema.ColumnMetadata;
|
||||||
import org.apache.cassandra.schema.TableMetadata;
|
import org.apache.cassandra.schema.TableMetadata;
|
||||||
|
|
@ -62,6 +70,11 @@ public abstract class Operation
|
||||||
this.t = t;
|
this.t = t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Term term()
|
||||||
|
{
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
public void addFunctionsTo(List<Function> functions)
|
public void addFunctionsTo(List<Function> functions)
|
||||||
{
|
{
|
||||||
if (t != null)
|
if (t != null)
|
||||||
|
|
@ -69,8 +82,7 @@ public abstract class Operation
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return whether the operation requires a read of the previous value to be executed
|
* @return whether the operation requires a read of the existing value to be executed
|
||||||
* (only lists setterByIdx, discard and discardByIdx requires that).
|
|
||||||
*/
|
*/
|
||||||
public boolean requiresRead()
|
public boolean requiresRead()
|
||||||
{
|
{
|
||||||
|
|
@ -178,7 +190,7 @@ public abstract class Operation
|
||||||
|
|
||||||
if (receiver.type.isCollection())
|
if (receiver.type.isCollection())
|
||||||
{
|
{
|
||||||
switch (((CollectionType) receiver.type).kind)
|
switch (((CollectionType<?>) receiver.type).kind)
|
||||||
{
|
{
|
||||||
case LIST:
|
case LIST:
|
||||||
return new Lists.Setter(receiver, v);
|
return new Lists.Setter(receiver, v);
|
||||||
|
|
@ -228,7 +240,7 @@ public abstract class Operation
|
||||||
else if (!(receiver.type.isMultiCell()))
|
else if (!(receiver.type.isMultiCell()))
|
||||||
throw new InvalidRequestException(String.format("Invalid operation (%s) for frozen collection column %s", toString(receiver), receiver.name));
|
throw new InvalidRequestException(String.format("Invalid operation (%s) for frozen collection column %s", toString(receiver), receiver.name));
|
||||||
|
|
||||||
switch (((CollectionType)receiver.type).kind)
|
switch (((CollectionType<?>)receiver.type).kind)
|
||||||
{
|
{
|
||||||
case LIST:
|
case LIST:
|
||||||
Term idx = selector.prepare(metadata.keyspace, Lists.indexSpecOf(receiver));
|
Term idx = selector.prepare(metadata.keyspace, Lists.indexSpecOf(receiver));
|
||||||
|
|
@ -328,7 +340,7 @@ public abstract class Operation
|
||||||
else if (!(receiver.type.isMultiCell()))
|
else if (!(receiver.type.isMultiCell()))
|
||||||
throw new InvalidRequestException(String.format("Invalid operation (%s) for frozen collection column %s", toString(receiver), receiver.name));
|
throw new InvalidRequestException(String.format("Invalid operation (%s) for frozen collection column %s", toString(receiver), receiver.name));
|
||||||
|
|
||||||
switch (((CollectionType)receiver.type).kind)
|
switch (((CollectionType<?>)receiver.type).kind)
|
||||||
{
|
{
|
||||||
case LIST:
|
case LIST:
|
||||||
return new Lists.Appender(receiver, value.prepare(metadata.keyspace, receiver));
|
return new Lists.Appender(receiver, value.prepare(metadata.keyspace, receiver));
|
||||||
|
|
@ -371,7 +383,7 @@ public abstract class Operation
|
||||||
}
|
}
|
||||||
|
|
||||||
public Operation prepare(TableMetadata metadata, ColumnMetadata receiver, boolean canReadExistingState) throws InvalidRequestException
|
public Operation prepare(TableMetadata metadata, ColumnMetadata receiver, boolean canReadExistingState) throws InvalidRequestException
|
||||||
{
|
{
|
||||||
if (!(receiver.type instanceof CollectionType))
|
if (!(receiver.type instanceof CollectionType))
|
||||||
{
|
{
|
||||||
if (canReadExistingState)
|
if (canReadExistingState)
|
||||||
|
|
@ -389,7 +401,7 @@ public abstract class Operation
|
||||||
else if (!(receiver.type.isMultiCell()))
|
else if (!(receiver.type.isMultiCell()))
|
||||||
throw new InvalidRequestException(String.format("Invalid operation (%s) for frozen collection column %s", toString(receiver), receiver.name));
|
throw new InvalidRequestException(String.format("Invalid operation (%s) for frozen collection column %s", toString(receiver), receiver.name));
|
||||||
|
|
||||||
switch (((CollectionType)receiver.type).kind)
|
switch (((CollectionType<?>)receiver.type).kind)
|
||||||
{
|
{
|
||||||
case LIST:
|
case LIST:
|
||||||
return new Lists.Discarder(receiver, value.prepare(metadata.keyspace, receiver));
|
return new Lists.Discarder(receiver, value.prepare(metadata.keyspace, receiver));
|
||||||
|
|
@ -400,7 +412,7 @@ public abstract class Operation
|
||||||
ColumnSpecification vr = new ColumnSpecification(receiver.ksName,
|
ColumnSpecification vr = new ColumnSpecification(receiver.ksName,
|
||||||
receiver.cfName,
|
receiver.cfName,
|
||||||
receiver.name,
|
receiver.name,
|
||||||
SetType.getInstance(((MapType)receiver.type).getKeysType(), false));
|
SetType.getInstance(((MapType<?, ?>) receiver.type).getKeysType(), true));
|
||||||
Term term;
|
Term term;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -502,7 +514,7 @@ public abstract class Operation
|
||||||
else if (!(receiver.type.isMultiCell()))
|
else if (!(receiver.type.isMultiCell()))
|
||||||
throw new InvalidRequestException(String.format("Invalid deletion operation for frozen collection column %s", receiver.name));
|
throw new InvalidRequestException(String.format("Invalid deletion operation for frozen collection column %s", receiver.name));
|
||||||
|
|
||||||
switch (((CollectionType)receiver.type).kind)
|
switch (((CollectionType<?>)receiver.type).kind)
|
||||||
{
|
{
|
||||||
case LIST:
|
case LIST:
|
||||||
Term idx = element.prepare(keyspace, Lists.indexSpecOf(receiver));
|
Term idx = element.prepare(keyspace, Lists.indexSpecOf(receiver));
|
||||||
|
|
|
||||||
|
|
@ -21,10 +21,12 @@ import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.google.common.collect.Iterators;
|
||||||
|
|
||||||
import org.apache.cassandra.cql3.functions.Function;
|
import org.apache.cassandra.cql3.functions.Function;
|
||||||
import org.apache.cassandra.cql3.statements.StatementType;
|
import org.apache.cassandra.cql3.statements.StatementType;
|
||||||
|
import org.apache.cassandra.cql3.transactions.ReferenceOperation;
|
||||||
import com.google.common.collect.Iterators;
|
import org.apache.cassandra.schema.ColumnMetadata;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A set of <code>Operation</code>s.
|
* A set of <code>Operation</code>s.
|
||||||
|
|
@ -47,11 +49,34 @@ public final class Operations implements Iterable<Operation>
|
||||||
*/
|
*/
|
||||||
private final List<Operation> staticOperations = new ArrayList<>();
|
private final List<Operation> staticOperations = new ArrayList<>();
|
||||||
|
|
||||||
|
private final List<ReferenceOperation> regularSubstitutions = new ArrayList<>();
|
||||||
|
private final List<ReferenceOperation> staticSubstitutions = new ArrayList<>();
|
||||||
|
|
||||||
public Operations(StatementType type)
|
public Operations(StatementType type)
|
||||||
{
|
{
|
||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void migrateReadRequiredOperations()
|
||||||
|
{
|
||||||
|
migrateReadRequiredOperations(staticOperations, staticSubstitutions);
|
||||||
|
migrateReadRequiredOperations(regularOperations, regularSubstitutions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void migrateReadRequiredOperations(List<Operation> src, List<ReferenceOperation> dest)
|
||||||
|
{
|
||||||
|
Iterator<Operation> it = src.iterator();
|
||||||
|
while (it.hasNext())
|
||||||
|
{
|
||||||
|
Operation next = it.next();
|
||||||
|
if (next.requiresRead())
|
||||||
|
{
|
||||||
|
it.remove();
|
||||||
|
dest.add(ReferenceOperation.create(next));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if some of the operations apply to static columns.
|
* Checks if some of the operations apply to static columns.
|
||||||
*
|
*
|
||||||
|
|
@ -105,6 +130,14 @@ public final class Operations implements Iterable<Operation>
|
||||||
regularOperations.add(operation);
|
regularOperations.add(operation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void add(ColumnMetadata column, ReferenceOperation operation)
|
||||||
|
{
|
||||||
|
if (column.isStatic())
|
||||||
|
staticSubstitutions.add(operation);
|
||||||
|
else
|
||||||
|
regularSubstitutions.add(operation);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if one of the operations requires a read.
|
* Checks if one of the operations requires a read.
|
||||||
*
|
*
|
||||||
|
|
@ -143,4 +176,29 @@ public final class Operations implements Iterable<Operation>
|
||||||
regularOperations.forEach(p -> p.addFunctionsTo(functions));
|
regularOperations.forEach(p -> p.addFunctionsTo(functions));
|
||||||
staticOperations.forEach(p -> p.addFunctionsTo(functions));
|
staticOperations.forEach(p -> p.addFunctionsTo(functions));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<ReferenceOperation> allSubstitutions()
|
||||||
|
{
|
||||||
|
if (staticSubstitutions.isEmpty())
|
||||||
|
return regularSubstitutions;
|
||||||
|
|
||||||
|
if (regularSubstitutions.isEmpty())
|
||||||
|
return staticSubstitutions;
|
||||||
|
|
||||||
|
// Only create a new list if we actually have something to combine
|
||||||
|
List<ReferenceOperation> list = new ArrayList<>(staticSubstitutions.size() + regularSubstitutions.size());
|
||||||
|
list.addAll(staticSubstitutions);
|
||||||
|
list.addAll(regularSubstitutions);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ReferenceOperation> regularSubstitutions()
|
||||||
|
{
|
||||||
|
return regularSubstitutions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ReferenceOperation> staticSubstitutions()
|
||||||
|
{
|
||||||
|
return staticSubstitutions;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import com.google.common.collect.RangeSet;
|
import com.google.common.collect.RangeSet;
|
||||||
|
|
||||||
|
|
@ -39,13 +40,18 @@ import org.apache.cassandra.db.marshal.MultiElementType;
|
||||||
import org.apache.cassandra.db.marshal.SetType;
|
import org.apache.cassandra.db.marshal.SetType;
|
||||||
import org.apache.cassandra.db.rows.CellPath;
|
import org.apache.cassandra.db.rows.CellPath;
|
||||||
import org.apache.cassandra.db.rows.ComplexColumnData;
|
import org.apache.cassandra.db.rows.ComplexColumnData;
|
||||||
|
import org.apache.cassandra.io.util.DataInputPlus;
|
||||||
|
import org.apache.cassandra.io.util.DataOutputPlus;
|
||||||
import org.apache.cassandra.schema.ColumnMetadata;
|
import org.apache.cassandra.schema.ColumnMetadata;
|
||||||
import org.apache.cassandra.serializers.ListSerializer;
|
import org.apache.cassandra.serializers.ListSerializer;
|
||||||
import org.apache.cassandra.utils.ByteBufferUtil;
|
import org.apache.cassandra.utils.ByteBufferUtil;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
|
||||||
import static org.apache.cassandra.cql3.statements.RequestValidations.checkFalse;
|
import static org.apache.cassandra.cql3.statements.RequestValidations.checkFalse;
|
||||||
import static org.apache.cassandra.cql3.statements.RequestValidations.checkTrue;
|
import static org.apache.cassandra.cql3.statements.RequestValidations.checkTrue;
|
||||||
import static org.apache.cassandra.cql3.statements.RequestValidations.invalidRequest;
|
import static org.apache.cassandra.cql3.statements.RequestValidations.invalidRequest;
|
||||||
|
import static org.apache.cassandra.db.TypeSizes.sizeofUnsignedVInt;
|
||||||
|
|
||||||
public enum Operator
|
public enum Operator
|
||||||
{
|
{
|
||||||
|
|
@ -828,6 +834,25 @@ public enum Operator
|
||||||
BINARY, TERNARY, MULTI_VALUE;
|
BINARY, TERNARY, MULTI_VALUE;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private static final Operator[] idToOperatorMapping;
|
||||||
|
|
||||||
|
static
|
||||||
|
{
|
||||||
|
Operator[] operators = values();
|
||||||
|
int maxId = Stream.of(operators)
|
||||||
|
.map(Operator::getValue)
|
||||||
|
.max(Integer::compareTo)
|
||||||
|
.get();
|
||||||
|
|
||||||
|
idToOperatorMapping = new Operator[maxId + 1];
|
||||||
|
for (Operator operator : operators)
|
||||||
|
{
|
||||||
|
if (null != idToOperatorMapping[operator.b])
|
||||||
|
throw new IllegalStateException("Duplicate Operator id " + operator.b);
|
||||||
|
idToOperatorMapping[operator.b] = operator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The binary representation of this <code>Enum</code> value.
|
* The binary representation of this <code>Enum</code> value.
|
||||||
*/
|
*/
|
||||||
|
|
@ -853,6 +878,17 @@ public enum Operator
|
||||||
output.writeInt(getValue());
|
output.writeInt(getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write the serialized version of this <code>Operator</code> to the specified output.
|
||||||
|
*
|
||||||
|
* @param output the output to write to
|
||||||
|
* @throws IOException if an I/O problem occurs while writing to the specified output
|
||||||
|
*/
|
||||||
|
public void writeToUnsignedVInt(DataOutputPlus output) throws IOException
|
||||||
|
{
|
||||||
|
output.writeUnsignedVInt32(b);
|
||||||
|
}
|
||||||
|
|
||||||
public int getValue()
|
public int getValue()
|
||||||
{
|
{
|
||||||
return b;
|
return b;
|
||||||
|
|
@ -885,12 +921,27 @@ public enum Operator
|
||||||
*/
|
*/
|
||||||
public static Operator readFrom(DataInput input) throws IOException
|
public static Operator readFrom(DataInput input) throws IOException
|
||||||
{
|
{
|
||||||
int b = input.readInt();
|
return fromBinary(input.readInt());
|
||||||
for (Operator operator : values())
|
}
|
||||||
if (operator.b == b)
|
|
||||||
return operator;
|
|
||||||
|
|
||||||
throw new IOException(String.format("Cannot resolve Relation.Type from binary representation: %s", b));
|
/**
|
||||||
|
* Deserializes a <code>Operator</code> instance from the specified input.
|
||||||
|
*
|
||||||
|
* @param input the input to read from
|
||||||
|
* @return the <code>Operator</code> instance deserialized
|
||||||
|
* @throws IOException if a problem occurs while deserializing the <code>Type</code> instance.
|
||||||
|
*/
|
||||||
|
public static Operator readFromUnsignedVInt(DataInputPlus input) throws IOException
|
||||||
|
{
|
||||||
|
return fromBinary(input.readUnsignedVInt32());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Operator fromBinary(int b) throws IOException
|
||||||
|
{
|
||||||
|
checkArgument(b > -1, "b must be > -1 to be a valid Operator id");
|
||||||
|
if (b > idToOperatorMapping.length)
|
||||||
|
throw new IOException(String.format("Cannot resolve Operator from binary representation: %s", b));
|
||||||
|
return idToOperatorMapping[b];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1149,4 +1200,9 @@ public enum Operator
|
||||||
}
|
}
|
||||||
return String.format("%s %s %s", leftOperand, this, rightOperand);
|
return String.format("%s %s %s", leftOperand, this, rightOperand);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long sizeAsUnsignedVInt()
|
||||||
|
{
|
||||||
|
return sizeofUnsignedVInt(b);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ import org.apache.cassandra.cql3.statements.BatchStatement;
|
||||||
import org.apache.cassandra.cql3.statements.ModificationStatement;
|
import org.apache.cassandra.cql3.statements.ModificationStatement;
|
||||||
import org.apache.cassandra.cql3.statements.QualifiedStatement;
|
import org.apache.cassandra.cql3.statements.QualifiedStatement;
|
||||||
import org.apache.cassandra.cql3.statements.SelectStatement;
|
import org.apache.cassandra.cql3.statements.SelectStatement;
|
||||||
|
import org.apache.cassandra.cql3.statements.TransactionStatement;
|
||||||
import org.apache.cassandra.cql3.statements.schema.AlterSchemaStatement;
|
import org.apache.cassandra.cql3.statements.schema.AlterSchemaStatement;
|
||||||
import org.apache.cassandra.db.ConsistencyLevel;
|
import org.apache.cassandra.db.ConsistencyLevel;
|
||||||
import org.apache.cassandra.db.DecoratedKey;
|
import org.apache.cassandra.db.DecoratedKey;
|
||||||
|
|
@ -297,6 +298,8 @@ public class QueryProcessor implements QueryHandler
|
||||||
return processNodeLocalWrite(statement, queryState, options);
|
return processNodeLocalWrite(statement, queryState, options);
|
||||||
else if (statement instanceof SelectStatement)
|
else if (statement instanceof SelectStatement)
|
||||||
return processNodeLocalSelect((SelectStatement) statement, queryState, options);
|
return processNodeLocalSelect((SelectStatement) statement, queryState, options);
|
||||||
|
else if (statement instanceof TransactionStatement)
|
||||||
|
return statement.executeLocally(queryState, options);
|
||||||
else
|
else
|
||||||
throw new InvalidRequestException("NODE_LOCAL consistency level can only be used with BATCH, UPDATE, INSERT, DELETE, and SELECT statements");
|
throw new InvalidRequestException("NODE_LOCAL consistency level can only be used with BATCH, UPDATE, INSERT, DELETE, and SELECT statements");
|
||||||
}
|
}
|
||||||
|
|
@ -1055,10 +1058,9 @@ public class QueryProcessor implements QueryHandler
|
||||||
statementKsName = selectStatement.keyspace();
|
statementKsName = selectStatement.keyspace();
|
||||||
statementCfName = selectStatement.table();
|
statementCfName = selectStatement.table();
|
||||||
}
|
}
|
||||||
else if (statement instanceof BatchStatement)
|
else if (statement instanceof CQLStatement.CompositeCQLStatement)
|
||||||
{
|
{
|
||||||
BatchStatement batchStatement = ((BatchStatement) statement);
|
for (CQLStatement stmt : ((CQLStatement.CompositeCQLStatement) statement).getStatements())
|
||||||
for (ModificationStatement stmt : batchStatement.getStatements())
|
|
||||||
{
|
{
|
||||||
if (shouldInvalidate(ksName, cfName, stmt))
|
if (shouldInvalidate(ksName, cfName, stmt))
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,6 @@ import java.util.Objects;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import org.apache.cassandra.cql3.statements.SelectStatement;
|
|
||||||
import org.apache.cassandra.db.marshal.AbstractType;
|
import org.apache.cassandra.db.marshal.AbstractType;
|
||||||
import org.apache.cassandra.service.pager.PagingState;
|
import org.apache.cassandra.service.pager.PagingState;
|
||||||
import org.apache.cassandra.transport.CBCodec;
|
import org.apache.cassandra.transport.CBCodec;
|
||||||
|
|
@ -314,8 +313,8 @@ public class ResultSet
|
||||||
|
|
||||||
public static ResultMetadata fromPrepared(CQLStatement statement)
|
public static ResultMetadata fromPrepared(CQLStatement statement)
|
||||||
{
|
{
|
||||||
if (statement instanceof SelectStatement)
|
if (statement instanceof CQLStatement.ReturningCQLStatement)
|
||||||
return ((SelectStatement)statement).getResultMetadata();
|
return ((CQLStatement.ReturningCQLStatement) statement).getResultMetadata();
|
||||||
|
|
||||||
return ResultSet.ResultMetadata.EMPTY;
|
return ResultSet.ResultMetadata.EMPTY;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,6 @@ import org.apache.cassandra.utils.TimeUUID;
|
||||||
public class UpdateParameters
|
public class UpdateParameters
|
||||||
{
|
{
|
||||||
public final TableMetadata metadata;
|
public final TableMetadata metadata;
|
||||||
public final RegularAndStaticColumns updatedColumns;
|
|
||||||
public final ClientState clientState;
|
public final ClientState clientState;
|
||||||
public final QueryOptions options;
|
public final QueryOptions options;
|
||||||
|
|
||||||
|
|
@ -47,7 +46,7 @@ public class UpdateParameters
|
||||||
|
|
||||||
private final DeletionTime deletionTime;
|
private final DeletionTime deletionTime;
|
||||||
|
|
||||||
// For lists operation that require a read-before-write. Will be null otherwise.
|
// Holds data for operations that require a read-before-write. Will be null otherwise.
|
||||||
private final Map<DecoratedKey, Partition> prefetchedRows;
|
private final Map<DecoratedKey, Partition> prefetchedRows;
|
||||||
|
|
||||||
private Row.Builder staticBuilder;
|
private Row.Builder staticBuilder;
|
||||||
|
|
@ -57,17 +56,14 @@ public class UpdateParameters
|
||||||
private Row.Builder builder;
|
private Row.Builder builder;
|
||||||
|
|
||||||
public UpdateParameters(TableMetadata metadata,
|
public UpdateParameters(TableMetadata metadata,
|
||||||
RegularAndStaticColumns updatedColumns,
|
|
||||||
ClientState clientState,
|
ClientState clientState,
|
||||||
QueryOptions options,
|
QueryOptions options,
|
||||||
long timestamp,
|
long timestamp,
|
||||||
long nowInSec,
|
long nowInSec,
|
||||||
int ttl,
|
int ttl,
|
||||||
Map<DecoratedKey, Partition> prefetchedRows)
|
Map<DecoratedKey, Partition> prefetchedRows) throws InvalidRequestException
|
||||||
throws InvalidRequestException
|
|
||||||
{
|
{
|
||||||
this.metadata = metadata;
|
this.metadata = metadata;
|
||||||
this.updatedColumns = updatedColumns;
|
|
||||||
this.clientState = clientState;
|
this.clientState = clientState;
|
||||||
this.options = options;
|
this.options = options;
|
||||||
|
|
||||||
|
|
@ -123,10 +119,20 @@ public class UpdateParameters
|
||||||
|
|
||||||
public void addPrimaryKeyLivenessInfo()
|
public void addPrimaryKeyLivenessInfo()
|
||||||
{
|
{
|
||||||
builder.addPrimaryKeyLivenessInfo(LivenessInfo.create(timestamp, ttl, nowInSec));
|
addPrimaryKeyLivenessInfo(LivenessInfo.create(timestamp, ttl, nowInSec));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addPrimaryKeyLivenessInfo(LivenessInfo info)
|
||||||
|
{
|
||||||
|
builder.addPrimaryKeyLivenessInfo(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addRowDeletion()
|
public void addRowDeletion()
|
||||||
|
{
|
||||||
|
addRowDeletion(Row.Deletion.regular(deletionTime));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addRowDeletion(Row.Deletion deletion)
|
||||||
{
|
{
|
||||||
// For compact tables, at the exclusion of the static row (of static compact tables), each row ever has a single column,
|
// For compact tables, at the exclusion of the static row (of static compact tables), each row ever has a single column,
|
||||||
// the "compact" one. As such, deleting the row or deleting that single cell is equivalent. We favor the later
|
// the "compact" one. As such, deleting the row or deleting that single cell is equivalent. We favor the later
|
||||||
|
|
@ -134,7 +140,7 @@ public class UpdateParameters
|
||||||
if (metadata.isCompactTable() && builder.clustering() != Clustering.STATIC_CLUSTERING)
|
if (metadata.isCompactTable() && builder.clustering() != Clustering.STATIC_CLUSTERING)
|
||||||
addTombstone(((TableMetadata.CompactTableMetadata) metadata).compactValueColumn);
|
addTombstone(((TableMetadata.CompactTableMetadata) metadata).compactValueColumn);
|
||||||
else
|
else
|
||||||
builder.addRowDeletion(Row.Deletion.regular(deletionTime));
|
builder.addRowDeletion(deletion);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addTombstone(ColumnMetadata column) throws InvalidRequestException
|
public void addTombstone(ColumnMetadata column) throws InvalidRequestException
|
||||||
|
|
@ -179,6 +185,14 @@ public class UpdateParameters
|
||||||
return cell;
|
return cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addRow(Row row)
|
||||||
|
{
|
||||||
|
newRow(row.clustering());
|
||||||
|
addRowDeletion(row.deletion());
|
||||||
|
addPrimaryKeyLivenessInfo(row.primaryKeyLivenessInfo());
|
||||||
|
row.cells().forEach(builder::addCell);
|
||||||
|
}
|
||||||
|
|
||||||
private void validateColumnSize(ColumnMetadata column, ByteBuffer value)
|
private void validateColumnSize(ColumnMetadata column, ByteBuffer value)
|
||||||
{
|
{
|
||||||
CQL3Type cql3Type = column.type.asCQL3Type();
|
CQL3Type cql3Type = column.type.asCQL3Type();
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.cassandra.cql3.conditions;
|
package org.apache.cassandra.cql3.conditions;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
|
@ -42,11 +43,17 @@ import org.apache.cassandra.db.rows.Cell;
|
||||||
import org.apache.cassandra.db.rows.ColumnData;
|
import org.apache.cassandra.db.rows.ColumnData;
|
||||||
import org.apache.cassandra.db.rows.ComplexColumnData;
|
import org.apache.cassandra.db.rows.ComplexColumnData;
|
||||||
import org.apache.cassandra.db.rows.Row;
|
import org.apache.cassandra.db.rows.Row;
|
||||||
|
import org.apache.cassandra.io.IVersionedSerializer;
|
||||||
|
import org.apache.cassandra.io.util.DataInputPlus;
|
||||||
|
import org.apache.cassandra.io.util.DataOutputPlus;
|
||||||
import org.apache.cassandra.schema.ColumnMetadata;
|
import org.apache.cassandra.schema.ColumnMetadata;
|
||||||
import org.apache.cassandra.schema.TableMetadata;
|
import org.apache.cassandra.schema.TableMetadata;
|
||||||
import org.apache.cassandra.utils.ByteBufferUtil;
|
import org.apache.cassandra.utils.ByteBufferUtil;
|
||||||
|
|
||||||
import static org.apache.cassandra.cql3.statements.RequestValidations.*;
|
import static org.apache.cassandra.cql3.statements.RequestValidations.*;
|
||||||
|
import static org.apache.cassandra.db.TypeSizes.sizeofUnsignedVInt;
|
||||||
|
import static org.apache.cassandra.service.accord.AccordSerializers.columnMetadataSerializer;
|
||||||
|
import static org.apache.cassandra.utils.ByteBufferUtil.nullableByteBufferSerializer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A CQL3 condition on the value of a column or collection element. For example, "UPDATE .. IF a = 0".
|
* A CQL3 condition on the value of a column or collection element. For example, "UPDATE .. IF a = 0".
|
||||||
|
|
@ -171,6 +178,41 @@ public final class ColumnCondition
|
||||||
return operator.buildCQLString(columnsExpression, values);
|
return operator.buildCQLString(columnsExpression, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private interface BoundSerializer<T extends Bound>
|
||||||
|
{
|
||||||
|
default void serialize(T bound, DataOutputPlus out, int version) throws IOException {}
|
||||||
|
Bound deserialize(DataInputPlus in, int version, ColumnMetadata column, Operator operator, ByteBuffer value) throws IOException;
|
||||||
|
default long serializedSize(T condition, int version) { return 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
enum BoundKind
|
||||||
|
{
|
||||||
|
Simple(0, SimpleBound.serializer),
|
||||||
|
ElementOrFieldAccess(1, ElementOrFieldAccessBound.serializer),
|
||||||
|
MultiCell(2, MultiCellBound.serializer);
|
||||||
|
|
||||||
|
private final int id;
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
private final BoundSerializer serializer;
|
||||||
|
|
||||||
|
BoundKind(int id, BoundSerializer<?> serializer)
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
this.serializer = serializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BoundKind valueOf(int id)
|
||||||
|
{
|
||||||
|
switch (id)
|
||||||
|
{
|
||||||
|
case 0: return BoundKind.Simple;
|
||||||
|
case 1: return BoundKind.ElementOrFieldAccess;
|
||||||
|
case 2: return BoundKind.MultiCell;
|
||||||
|
default: throw new IllegalArgumentException("Unknown id: " + id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static abstract class Bound
|
public static abstract class Bound
|
||||||
{
|
{
|
||||||
protected final ColumnMetadata column;
|
protected final ColumnMetadata column;
|
||||||
|
|
@ -188,14 +230,55 @@ public final class ColumnCondition
|
||||||
* Validates whether this condition applies to {@code current}.
|
* Validates whether this condition applies to {@code current}.
|
||||||
*/
|
*/
|
||||||
public abstract boolean appliesTo(Row row);
|
public abstract boolean appliesTo(Row row);
|
||||||
|
|
||||||
|
protected abstract BoundKind kind();
|
||||||
|
|
||||||
|
public static final IVersionedSerializer<Bound> serializer = new IVersionedSerializer<>()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void serialize(Bound bound, DataOutputPlus out, int version) throws IOException
|
||||||
|
{
|
||||||
|
columnMetadataSerializer.serialize(bound.column, out, version);
|
||||||
|
bound.operator.writeToUnsignedVInt(out);
|
||||||
|
nullableByteBufferSerializer.serialize(bound.value, out, version);
|
||||||
|
BoundKind kind = bound.kind();
|
||||||
|
out.writeUnsignedVInt32(kind.ordinal());
|
||||||
|
kind.serializer.serialize(bound, out, version);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Bound deserialize(DataInputPlus in, int version) throws IOException
|
||||||
|
{
|
||||||
|
ColumnMetadata column = columnMetadataSerializer.deserialize(in, version);
|
||||||
|
Operator operator = Operator.readFromUnsignedVInt(in);
|
||||||
|
ByteBuffer value = nullableByteBufferSerializer.deserialize(in, version);
|
||||||
|
BoundKind boundKind = BoundKind.valueOf(in.readUnsignedVInt32());
|
||||||
|
return boundKind.serializer.deserialize(in, version, column, operator, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public long serializedSize(Bound bound, int version)
|
||||||
|
{
|
||||||
|
BoundKind kind = bound.kind();
|
||||||
|
return columnMetadataSerializer.serializedSize(bound.column, version)
|
||||||
|
+ bound.operator.sizeAsUnsignedVInt()
|
||||||
|
+ nullableByteBufferSerializer.serializedSize(bound.value, version)
|
||||||
|
+ sizeofUnsignedVInt(kind.ordinal())
|
||||||
|
+ kind.serializer.serializedSize(bound, version);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A condition on a single non-collection column.
|
* A condition on a single non-collection column.
|
||||||
*/
|
*/
|
||||||
private static final class SimpleBound extends Bound
|
public static class SimpleBound extends Bound
|
||||||
{
|
{
|
||||||
private SimpleBound(ColumnMetadata column, Operator operator, ByteBuffer value)
|
private static final BoundSerializer<SimpleBound> serializer = (in, version, column, operator, value) -> new SimpleBound(column, operator, value);
|
||||||
|
|
||||||
|
public SimpleBound(ColumnMetadata column, Operator operator, ByteBuffer value)
|
||||||
{
|
{
|
||||||
super(column, operator, value);
|
super(column, operator, value);
|
||||||
}
|
}
|
||||||
|
|
@ -206,7 +289,7 @@ public final class ColumnCondition
|
||||||
return operator.isSatisfiedBy(column.type, rowValue(row), value);
|
return operator.isSatisfiedBy(column.type, rowValue(row), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ByteBuffer rowValue(Row row)
|
protected ByteBuffer rowValue(Row row)
|
||||||
{
|
{
|
||||||
// If we're asking for a given cell, and we didn't get any row from our read, it's
|
// If we're asking for a given cell, and we didn't get any row from our read, it's
|
||||||
// the same as not having said cell.
|
// the same as not having said cell.
|
||||||
|
|
@ -216,13 +299,70 @@ public final class ColumnCondition
|
||||||
Cell<?> c = row.getCell(column);
|
Cell<?> c = row.getCell(column);
|
||||||
return c == null ? null : c.buffer();
|
return c == null ? null : c.buffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected BoundKind kind()
|
||||||
|
{
|
||||||
|
return BoundKind.Simple;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o)
|
||||||
|
{
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
SimpleBound bound = (SimpleBound) o;
|
||||||
|
return column.equals(bound.column) && operator == bound.operator && Objects.equals(value, bound.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode()
|
||||||
|
{
|
||||||
|
return Objects.hash(column, operator, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SimpleClusteringBound extends SimpleBound
|
||||||
|
{
|
||||||
|
public SimpleClusteringBound(ColumnMetadata column, Operator operator, ByteBuffer value)
|
||||||
|
{
|
||||||
|
super(column, operator, value);
|
||||||
|
assert column.isClusteringColumn() : String.format("Column must be a clustering column, but given %s", column);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ByteBuffer rowValue(Row row)
|
||||||
|
{
|
||||||
|
return row == null ? null : row.clustering().bufferAt(column.position());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A condition on a collection element or a UDT field.
|
* A condition on a collection element or a UDT field.
|
||||||
*/
|
*/
|
||||||
private static final class ElementOrFieldAccessBound extends Bound
|
public static final class ElementOrFieldAccessBound extends Bound
|
||||||
{
|
{
|
||||||
|
private static final BoundSerializer<ElementOrFieldAccessBound> serializer = new BoundSerializer<>()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void serialize(ElementOrFieldAccessBound bound, DataOutputPlus out, int version) throws IOException
|
||||||
|
{
|
||||||
|
nullableByteBufferSerializer.serialize(bound.keyOrIndex, out, version);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Bound deserialize(DataInputPlus in, int version, ColumnMetadata column, Operator operator, ByteBuffer value) throws IOException
|
||||||
|
{
|
||||||
|
ByteBuffer keyOrIndex = nullableByteBufferSerializer.deserialize(in, version);
|
||||||
|
return new ElementOrFieldAccessBound(column, keyOrIndex, operator, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long serializedSize(ElementOrFieldAccessBound condition, int version)
|
||||||
|
{
|
||||||
|
return nullableByteBufferSerializer.serializedSize(condition.keyOrIndex, version);
|
||||||
|
}
|
||||||
|
};
|
||||||
/**
|
/**
|
||||||
* The collection element or UDT field type.
|
* The collection element or UDT field type.
|
||||||
*/
|
*/
|
||||||
|
|
@ -234,16 +374,22 @@ public final class ColumnCondition
|
||||||
private final ByteBuffer keyOrIndex;
|
private final ByteBuffer keyOrIndex;
|
||||||
|
|
||||||
|
|
||||||
private ElementOrFieldAccessBound(ColumnMetadata column,
|
public ElementOrFieldAccessBound(ColumnMetadata column,
|
||||||
ByteBuffer keyOrIndex,
|
ByteBuffer keyOrIndex,
|
||||||
Operator operator,
|
Operator operator,
|
||||||
ByteBuffer value)
|
ByteBuffer value)
|
||||||
{
|
{
|
||||||
super(column, operator, value);
|
super(column, operator, value);
|
||||||
this.elementType = ((MultiElementType<?>) column.type).elementType(keyOrIndex);
|
this.elementType = ((MultiElementType<?>) column.type).elementType(keyOrIndex);
|
||||||
this.keyOrIndex = keyOrIndex;
|
this.keyOrIndex = keyOrIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected BoundKind kind()
|
||||||
|
{
|
||||||
|
return BoundKind.ElementOrFieldAccess;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean appliesTo(Row row)
|
public boolean appliesTo(Row row)
|
||||||
{
|
{
|
||||||
|
|
@ -260,17 +406,40 @@ public final class ColumnCondition
|
||||||
{
|
{
|
||||||
return row == null ? null : row.getColumnData(column);
|
return row == null ? null : row.getColumnData(column);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o)
|
||||||
|
{
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
ElementOrFieldAccessBound bound = (ElementOrFieldAccessBound) o;
|
||||||
|
return column.equals(bound.column) && operator == bound.operator && Objects.equals(value, bound.value) && Objects.equals(keyOrIndex, bound.keyOrIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode()
|
||||||
|
{
|
||||||
|
return Objects.hash(column, operator, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A condition on a multicell column.
|
* A condition on a multicell column.
|
||||||
*/
|
*/
|
||||||
private static final class MultiCellBound extends Bound
|
public static final class MultiCellBound extends Bound
|
||||||
{
|
{
|
||||||
|
private static final BoundSerializer<MultiCellBound> serializer = (in, version, column, operator, value) -> new MultiCellBound(column, operator, value);
|
||||||
|
|
||||||
public MultiCellBound(ColumnMetadata column, Operator operator, ByteBuffer value)
|
public MultiCellBound(ColumnMetadata column, Operator operator, ByteBuffer value)
|
||||||
{
|
{
|
||||||
super(column, operator, value);
|
super(column, operator, value);
|
||||||
assert column.type.isMultiCell();
|
assert column.type.isMultiCell() : String.format("Unexpected type: %s", column.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected BoundKind kind()
|
||||||
|
{
|
||||||
|
return BoundKind.MultiCell;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean appliesTo(Row row)
|
public boolean appliesTo(Row row)
|
||||||
|
|
@ -278,6 +447,21 @@ public final class ColumnCondition
|
||||||
ComplexColumnData columnData = row == null ? null : row.getComplexColumnData(column);
|
ComplexColumnData columnData = row == null ? null : row.getComplexColumnData(column);
|
||||||
return operator.isSatisfiedBy((MultiElementType<?>) column.type, columnData, value);
|
return operator.isSatisfiedBy((MultiElementType<?>) column.type, columnData, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o)
|
||||||
|
{
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
MultiCellBound bound = (MultiCellBound) o;
|
||||||
|
return column.equals(bound.column) && operator == bound.operator && Objects.equals(value, bound.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode()
|
||||||
|
{
|
||||||
|
return Objects.hash(column, operator, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Raw
|
public static class Raw
|
||||||
|
|
|
||||||
|
|
@ -868,13 +868,23 @@ public final class StatementRestrictions
|
||||||
*
|
*
|
||||||
* @return <code>true</code> if all the primary key columns are restricted by an equality relation.
|
* @return <code>true</code> if all the primary key columns are restricted by an equality relation.
|
||||||
*/
|
*/
|
||||||
public boolean hasAllPKColumnsRestrictedByEqualities()
|
public boolean hasAllPrimaryKeyColumnsRestrictedByEqualities()
|
||||||
|
{
|
||||||
|
return hasAllPartitionKeyColumnsRestrictedByEqualities()
|
||||||
|
&& !hasUnrestrictedClusteringColumns()
|
||||||
|
&& (clusteringColumnsRestrictions.hasOnlyEqualityRestrictions());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that all the partition key columns are restricted by an equality relation ('=' or 'IN').
|
||||||
|
*
|
||||||
|
* @return <code>true</code> if all the partition key columns are restricted by an equality relation.
|
||||||
|
*/
|
||||||
|
public boolean hasAllPartitionKeyColumnsRestrictedByEqualities()
|
||||||
{
|
{
|
||||||
return !isPartitionKeyRestrictionsOnToken()
|
return !isPartitionKeyRestrictionsOnToken()
|
||||||
&& !partitionKeyRestrictions.hasUnrestrictedPartitionKeyComponents()
|
&& !partitionKeyRestrictions.hasUnrestrictedPartitionKeyComponents()
|
||||||
&& (partitionKeyRestrictions.hasOnlyEqualityRestrictions())
|
&& (partitionKeyRestrictions.hasOnlyEqualityRestrictions());
|
||||||
&& !hasUnrestrictedClusteringColumns()
|
|
||||||
&& (clusteringColumnsRestrictions.hasOnlyEqualityRestrictions());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -577,8 +577,8 @@ public interface Selectable extends AssignmentTestable
|
||||||
|
|
||||||
public static class Raw implements Selectable.Raw
|
public static class Raw implements Selectable.Raw
|
||||||
{
|
{
|
||||||
private final Selectable.Raw selected;
|
public final Selectable.Raw selected;
|
||||||
private final FieldIdentifier field;
|
public final FieldIdentifier field;
|
||||||
|
|
||||||
public Raw(Selectable.Raw selected, FieldIdentifier field)
|
public Raw(Selectable.Raw selected, FieldIdentifier field)
|
||||||
{
|
{
|
||||||
|
|
@ -1471,8 +1471,8 @@ public interface Selectable extends AssignmentTestable
|
||||||
|
|
||||||
public static class Raw implements Selectable.Raw
|
public static class Raw implements Selectable.Raw
|
||||||
{
|
{
|
||||||
private final Selectable.Raw selected;
|
public final Selectable.Raw selected;
|
||||||
private final Term.Raw element;
|
public final Term.Raw element;
|
||||||
|
|
||||||
public Raw(Selectable.Raw selected, Term.Raw element)
|
public Raw(Selectable.Raw selected, Term.Raw element)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,12 @@
|
||||||
package org.apache.cassandra.cql3.selection;
|
package org.apache.cassandra.cql3.selection;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import com.google.common.base.MoreObjects;
|
import com.google.common.base.MoreObjects;
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
|
|
@ -134,6 +139,11 @@ public abstract class Selection
|
||||||
return resultMetadata;
|
return resultMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Selection.Selectors noopSelector()
|
||||||
|
{
|
||||||
|
return new SimpleSelectors();
|
||||||
|
}
|
||||||
|
|
||||||
public static Selection wildcard(TableMetadata table, boolean isJson, boolean returnStaticContentOnPartitionWithNoRows)
|
public static Selection wildcard(TableMetadata table, boolean isJson, boolean returnStaticContentOnPartitionWithNoRows)
|
||||||
{
|
{
|
||||||
List<ColumnMetadata> all = new ArrayList<>(table.columns().size());
|
List<ColumnMetadata> all = new ArrayList<>(table.columns().size());
|
||||||
|
|
@ -346,55 +356,72 @@ public abstract class Selection
|
||||||
return Arrays.asList(jsonRow);
|
return Arrays.asList(jsonRow);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static interface Selectors
|
public interface Selectors
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Returns the {@code ColumnFilter} corresponding to those selectors
|
* Returns the {@code ColumnFilter} corresponding to those selectors
|
||||||
*
|
*
|
||||||
* @return the {@code ColumnFilter} corresponding to those selectors
|
* @return the {@code ColumnFilter} corresponding to those selectors
|
||||||
*/
|
*/
|
||||||
public ColumnFilter getColumnFilter();
|
default ColumnFilter getColumnFilter() { return ColumnFilter.NONE; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if this Selectors perform some processing
|
* Checks if this Selectors perform some processing
|
||||||
* @return {@code true} if this Selectors perform some processing, {@code false} otherwise.
|
* @return {@code true} if this Selectors perform some processing, {@code false} otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean hasProcessing();
|
default boolean hasProcessing() { return false; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if one of the selectors perform some aggregations.
|
* Checks if one of the selectors perform some aggregations.
|
||||||
* @return {@code true} if one of the selectors perform some aggregations, {@code false} otherwise.
|
* @return {@code true} if one of the selectors perform some aggregations, {@code false} otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean isAggregate();
|
default boolean isAggregate() { return false; }
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the number of fetched columns
|
|
||||||
* @return the number of fetched columns
|
|
||||||
*/
|
|
||||||
public int numberOfFetchedColumns();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if one of the selectors collect TTLs.
|
* Checks if one of the selectors collect TTLs.
|
||||||
* @return {@code true} if one of the selectors collect TTLs, {@code false} otherwise.
|
* @return {@code true} if one of the selectors collect TTLs, {@code false} otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean collectTTLs();
|
default boolean collectTTLs() { return false; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if one of the selectors collects write timestamps.
|
* Checks if one of the selectors collects write timestamps.
|
||||||
* @return {@code true} if one of the selectors collects write timestamps, {@code false} otherwise.
|
* @return {@code true} if one of the selectors collects write timestamps, {@code false} otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean collectWritetimes();
|
default boolean collectWritetimes() { return false; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the current row of the specified <code>ResultSetBuilder</code>.
|
* Adds the current row of the specified <code>ResultSetBuilder</code>.
|
||||||
*
|
*
|
||||||
* @param input the input row
|
* @param input the input row
|
||||||
*/
|
*/
|
||||||
public void addInputRow(InputRow input);
|
void addInputRow(InputRow input);
|
||||||
|
|
||||||
public List<ByteBuffer> getOutputRow();
|
List<ByteBuffer> getOutputRow();
|
||||||
|
|
||||||
public void reset();
|
void reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SimpleSelectors implements Selectors
|
||||||
|
{
|
||||||
|
protected List<ByteBuffer> current;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addInputRow(InputRow input)
|
||||||
|
{
|
||||||
|
current = input.getValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ByteBuffer> getOutputRow()
|
||||||
|
{
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reset()
|
||||||
|
{
|
||||||
|
current = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Special cased selection for when only columns are selected.
|
// Special cased selection for when only columns are selected.
|
||||||
|
|
@ -466,15 +493,9 @@ public abstract class Selection
|
||||||
|
|
||||||
public Selectors newSelectors(QueryOptions options)
|
public Selectors newSelectors(QueryOptions options)
|
||||||
{
|
{
|
||||||
return new Selectors()
|
return new SimpleSelectors()
|
||||||
{
|
{
|
||||||
private List<ByteBuffer> current;
|
@Override
|
||||||
|
|
||||||
public void reset()
|
|
||||||
{
|
|
||||||
current = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<ByteBuffer> getOutputRow()
|
public List<ByteBuffer> getOutputRow()
|
||||||
{
|
{
|
||||||
if (isJson)
|
if (isJson)
|
||||||
|
|
@ -482,39 +503,6 @@ public abstract class Selection
|
||||||
return current;
|
return current;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addInputRow(InputRow input)
|
|
||||||
{
|
|
||||||
current = input.getValues();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isAggregate()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasProcessing()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int numberOfFetchedColumns()
|
|
||||||
{
|
|
||||||
return getColumns().size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean collectTTLs()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean collectWritetimes()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ColumnFilter getColumnFilter()
|
public ColumnFilter getColumnFilter()
|
||||||
{
|
{
|
||||||
|
|
@ -615,12 +603,6 @@ public abstract class Selection
|
||||||
selector.addInput(input);
|
selector.addInput(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int numberOfFetchedColumns()
|
|
||||||
{
|
|
||||||
return getColumns().size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean collectTTLs()
|
public boolean collectTTLs()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -414,7 +414,7 @@ public abstract class Selector
|
||||||
UserType udt = (UserType) type;
|
UserType udt = (UserType) type;
|
||||||
int size = udt.size();
|
int size = udt.size();
|
||||||
|
|
||||||
values[index] = udt.serializeForNativeProtocol(ccd.iterator(), protocolVersion);
|
values[index] = udt.serializeForNativeProtocol(ccd.iterator());
|
||||||
|
|
||||||
short fieldPosition = 0;
|
short fieldPosition = 0;
|
||||||
for (Cell<?> cell : ccd)
|
for (Cell<?> cell : ccd)
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ import static org.apache.cassandra.cql3.statements.RequestValidations.checkFalse
|
||||||
/**
|
/**
|
||||||
* A <code>BATCH</code> statement parsed from a CQL query.
|
* A <code>BATCH</code> statement parsed from a CQL query.
|
||||||
*/
|
*/
|
||||||
public class BatchStatement implements CQLStatement
|
public class BatchStatement implements CQLStatement.CompositeCQLStatement
|
||||||
{
|
{
|
||||||
public enum Type
|
public enum Type
|
||||||
{
|
{
|
||||||
|
|
@ -268,6 +268,7 @@ public class BatchStatement implements CQLStatement
|
||||||
statement.validate(state);
|
statement.validate(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public List<ModificationStatement> getStatements()
|
public List<ModificationStatement> getStatements()
|
||||||
{
|
{
|
||||||
return statements;
|
return statements;
|
||||||
|
|
@ -617,7 +618,7 @@ public class BatchStatement implements CQLStatement
|
||||||
return String.format("BatchStatement(type=%s, statements=%s)", type, statements);
|
return String.format("BatchStatement(type=%s, statements=%s)", type, statements);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Parsed extends QualifiedStatement
|
public static class Parsed extends QualifiedStatement.Composite
|
||||||
{
|
{
|
||||||
private final Type type;
|
private final Type type;
|
||||||
private final Attributes.Raw attrs;
|
private final Attributes.Raw attrs;
|
||||||
|
|
@ -625,21 +626,15 @@ public class BatchStatement implements CQLStatement
|
||||||
|
|
||||||
public Parsed(Type type, Attributes.Raw attrs, List<ModificationStatement.Parsed> parsedStatements)
|
public Parsed(Type type, Attributes.Raw attrs, List<ModificationStatement.Parsed> parsedStatements)
|
||||||
{
|
{
|
||||||
super(null);
|
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.attrs = attrs;
|
this.attrs = attrs;
|
||||||
this.parsedStatements = parsedStatements;
|
this.parsedStatements = parsedStatements;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not doing this in the constructor since we only need this for prepared statements
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isFullyQualified()
|
protected Iterable<? extends QualifiedStatement> getStatements()
|
||||||
{
|
{
|
||||||
for (ModificationStatement.Parsed statement : parsedStatements)
|
return parsedStatements;
|
||||||
if (!statement.isFullyQualified())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -17,28 +17,60 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.cassandra.cql3.statements;
|
package org.apache.cassandra.cql3.statements;
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
import org.apache.cassandra.db.marshal.TimeUUIDType;
|
import org.apache.cassandra.db.marshal.TimeUUIDType;
|
||||||
import org.apache.cassandra.index.IndexRegistry;
|
import org.apache.cassandra.index.IndexRegistry;
|
||||||
import org.apache.cassandra.schema.TableMetadata;
|
import org.apache.cassandra.schema.TableMetadata;
|
||||||
import org.apache.cassandra.cql3.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||||
|
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||||
|
|
||||||
|
import accord.api.Update;
|
||||||
|
import accord.primitives.Txn;
|
||||||
|
import org.apache.cassandra.cql3.QueryOptions;
|
||||||
|
import org.apache.cassandra.cql3.UpdateParameters;
|
||||||
import org.apache.cassandra.cql3.conditions.ColumnCondition;
|
import org.apache.cassandra.cql3.conditions.ColumnCondition;
|
||||||
import org.apache.cassandra.db.*;
|
import org.apache.cassandra.db.Clustering;
|
||||||
import org.apache.cassandra.db.filter.*;
|
import org.apache.cassandra.db.Columns;
|
||||||
import org.apache.cassandra.db.rows.Row;
|
import org.apache.cassandra.db.DecoratedKey;
|
||||||
|
import org.apache.cassandra.db.RegularAndStaticColumns;
|
||||||
|
import org.apache.cassandra.db.SinglePartitionReadCommand;
|
||||||
|
import org.apache.cassandra.db.Slice;
|
||||||
|
import org.apache.cassandra.db.Slices;
|
||||||
|
import org.apache.cassandra.db.filter.ClusteringIndexNamesFilter;
|
||||||
|
import org.apache.cassandra.db.filter.ClusteringIndexSliceFilter;
|
||||||
|
import org.apache.cassandra.db.filter.ColumnFilter;
|
||||||
|
import org.apache.cassandra.db.filter.DataLimits;
|
||||||
|
import org.apache.cassandra.db.filter.RowFilter;
|
||||||
import org.apache.cassandra.db.partitions.FilteredPartition;
|
import org.apache.cassandra.db.partitions.FilteredPartition;
|
||||||
import org.apache.cassandra.db.partitions.Partition;
|
import org.apache.cassandra.db.partitions.Partition;
|
||||||
import org.apache.cassandra.db.partitions.PartitionUpdate;
|
import org.apache.cassandra.db.partitions.PartitionUpdate;
|
||||||
|
import org.apache.cassandra.db.rows.Row;
|
||||||
|
import org.apache.cassandra.db.rows.RowIterator;
|
||||||
import org.apache.cassandra.exceptions.InvalidRequestException;
|
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||||
import org.apache.cassandra.service.CASRequest;
|
import org.apache.cassandra.service.CASRequest;
|
||||||
import org.apache.cassandra.service.ClientState;
|
import org.apache.cassandra.service.ClientState;
|
||||||
|
import org.apache.cassandra.service.accord.txn.TxnCondition;
|
||||||
|
import org.apache.cassandra.service.accord.txn.TxnData;
|
||||||
|
import org.apache.cassandra.service.accord.txn.TxnDataName;
|
||||||
|
import org.apache.cassandra.service.accord.txn.TxnQuery;
|
||||||
|
import org.apache.cassandra.service.accord.txn.TxnRead;
|
||||||
|
import org.apache.cassandra.service.accord.txn.TxnReference;
|
||||||
|
import org.apache.cassandra.service.accord.txn.TxnUpdate;
|
||||||
|
import org.apache.cassandra.service.accord.txn.TxnWrite;
|
||||||
import org.apache.cassandra.service.paxos.Ballot;
|
import org.apache.cassandra.service.paxos.Ballot;
|
||||||
import org.apache.cassandra.transport.Dispatcher;
|
import org.apache.cassandra.transport.Dispatcher;
|
||||||
import org.apache.cassandra.utils.TimeUUID;
|
import org.apache.cassandra.utils.TimeUUID;
|
||||||
|
|
||||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
import static org.apache.cassandra.service.accord.txn.TxnDataName.Kind.USER;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processed CAS conditions and update on potentially multiple rows of the same partition.
|
* Processed CAS conditions and update on potentially multiple rows of the same partition.
|
||||||
|
|
@ -259,9 +291,9 @@ public class CQL3CasRequest implements CASRequest
|
||||||
final long timeUuidMsb;
|
final long timeUuidMsb;
|
||||||
long timeUuidNanos;
|
long timeUuidNanos;
|
||||||
|
|
||||||
public CASUpdateParameters(TableMetadata metadata, RegularAndStaticColumns updatedColumns, ClientState state, QueryOptions options, long timestamp, long nowInSec, int ttl, Map<DecoratedKey, Partition> prefetchedRows, long timeUuidMsb, long timeUuidNanos) throws InvalidRequestException
|
public CASUpdateParameters(TableMetadata metadata, ClientState state, QueryOptions options, long timestamp, long nowInSec, int ttl, Map<DecoratedKey, Partition> prefetchedRows, long timeUuidMsb, long timeUuidNanos) throws InvalidRequestException
|
||||||
{
|
{
|
||||||
super(metadata, updatedColumns, state, options, timestamp, nowInSec, ttl, prefetchedRows);
|
super(metadata, state, options, timestamp, nowInSec, ttl, prefetchedRows);
|
||||||
this.timeUuidMsb = timeUuidMsb;
|
this.timeUuidMsb = timeUuidMsb;
|
||||||
this.timeUuidNanos = timeUuidNanos;
|
this.timeUuidNanos = timeUuidNanos;
|
||||||
}
|
}
|
||||||
|
|
@ -299,7 +331,7 @@ public class CQL3CasRequest implements CASRequest
|
||||||
{
|
{
|
||||||
Map<DecoratedKey, Partition> map = stmt.requiresRead() ? Collections.singletonMap(key, current) : null;
|
Map<DecoratedKey, Partition> map = stmt.requiresRead() ? Collections.singletonMap(key, current) : null;
|
||||||
CASUpdateParameters params =
|
CASUpdateParameters params =
|
||||||
new CASUpdateParameters(metadata, updateBuilder.columns(), state, options, timestamp, nowInSeconds,
|
new CASUpdateParameters(metadata, state, options, timestamp, nowInSeconds,
|
||||||
stmt.getTimeToLive(options), map, timeUuidMsb, timeUuidNanos);
|
stmt.getTimeToLive(options), map, timeUuidMsb, timeUuidNanos);
|
||||||
stmt.addUpdateForKey(updateBuilder, clustering, params);
|
stmt.addUpdateForKey(updateBuilder, clustering, params);
|
||||||
return params.timeUuidNanos;
|
return params.timeUuidNanos;
|
||||||
|
|
@ -329,7 +361,6 @@ public class CQL3CasRequest implements CASRequest
|
||||||
Map<DecoratedKey, Partition> map = stmt.requiresRead() ? Collections.singletonMap(key, current) : null;
|
Map<DecoratedKey, Partition> map = stmt.requiresRead() ? Collections.singletonMap(key, current) : null;
|
||||||
UpdateParameters params =
|
UpdateParameters params =
|
||||||
new UpdateParameters(metadata,
|
new UpdateParameters(metadata,
|
||||||
updateBuilder.columns(),
|
|
||||||
state,
|
state,
|
||||||
options,
|
options,
|
||||||
timestamp,
|
timestamp,
|
||||||
|
|
@ -350,6 +381,8 @@ public class CQL3CasRequest implements CASRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract boolean appliesTo(FilteredPartition current) throws InvalidRequestException;
|
public abstract boolean appliesTo(FilteredPartition current) throws InvalidRequestException;
|
||||||
|
|
||||||
|
public abstract TxnCondition asTxnCondition();
|
||||||
}
|
}
|
||||||
|
|
||||||
private interface ToCQL
|
private interface ToCQL
|
||||||
|
|
@ -374,6 +407,13 @@ public class CQL3CasRequest implements CASRequest
|
||||||
{
|
{
|
||||||
return "IF NOT EXISTS";
|
return "IF NOT EXISTS";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TxnCondition asTxnCondition()
|
||||||
|
{
|
||||||
|
TxnDataName txnDataName = new TxnDataName(USER, clustering, TxnRead.SERIAL_READ_NAME);
|
||||||
|
TxnReference txnReference = new TxnReference(txnDataName, null);
|
||||||
|
return new TxnCondition.Exists(txnReference, TxnCondition.Kind.IS_NULL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ExistCondition extends RowCondition implements ToCQL
|
private static class ExistCondition extends RowCondition implements ToCQL
|
||||||
|
|
@ -393,6 +433,13 @@ public class CQL3CasRequest implements CASRequest
|
||||||
{
|
{
|
||||||
return "IF EXISTS";
|
return "IF EXISTS";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TxnCondition asTxnCondition()
|
||||||
|
{
|
||||||
|
TxnDataName txnDataName = new TxnDataName(USER, clustering, TxnRead.SERIAL_READ_NAME);
|
||||||
|
TxnReference txnReference = new TxnReference(txnDataName, null);
|
||||||
|
return new TxnCondition.Exists(txnReference, TxnCondition.Kind.IS_NOT_NULL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ColumnsConditions extends RowCondition
|
private static class ColumnsConditions extends RowCondition
|
||||||
|
|
@ -422,6 +469,12 @@ public class CQL3CasRequest implements CASRequest
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TxnCondition asTxnCondition()
|
||||||
|
{
|
||||||
|
return new TxnCondition.ColumnConditionsAdapter(clustering, conditions);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -429,4 +482,56 @@ public class CQL3CasRequest implements CASRequest
|
||||||
{
|
{
|
||||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
|
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Txn toAccordTxn(ClientState clientState, long nowInSecs)
|
||||||
|
{
|
||||||
|
SinglePartitionReadCommand readCommand = readCommand(nowInSecs);
|
||||||
|
Update update = createUpdate(clientState);
|
||||||
|
// In a CAS request only one key is supported and writes
|
||||||
|
// can't be dependent on any data that is read (only conditions)
|
||||||
|
// so the only relevant keys are the read key
|
||||||
|
TxnRead read = TxnRead.createSerialRead(readCommand);
|
||||||
|
return new Txn.InMemory(read.keys(), read, TxnQuery.CONDITION, update);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Update createUpdate(ClientState clientState)
|
||||||
|
{
|
||||||
|
return new TxnUpdate(createWriteFragments(clientState), createCondition());
|
||||||
|
}
|
||||||
|
|
||||||
|
private TxnCondition createCondition()
|
||||||
|
{
|
||||||
|
List<TxnCondition> txnConditions = new ArrayList<>(conditions.size() + (staticConditions == null ? 0 : 1));
|
||||||
|
if (staticConditions != null)
|
||||||
|
{
|
||||||
|
txnConditions.add(staticConditions.asTxnCondition());
|
||||||
|
}
|
||||||
|
for (RowCondition condition : conditions.values())
|
||||||
|
txnConditions.add(condition.asTxnCondition());
|
||||||
|
// CAS forbids empty conditions
|
||||||
|
checkState(!txnConditions.isEmpty());
|
||||||
|
return conditions.size() == 1 ? txnConditions.get(0) : new TxnCondition.BooleanGroup(TxnCondition.Kind.AND, txnConditions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<TxnWrite.Fragment> createWriteFragments(ClientState state)
|
||||||
|
{
|
||||||
|
List<TxnWrite.Fragment> fragments = new ArrayList<>();
|
||||||
|
int idx = 0;
|
||||||
|
for (RowUpdate update : updates)
|
||||||
|
{
|
||||||
|
ModificationStatement modification = update.stmt;
|
||||||
|
QueryOptions options = update.options;
|
||||||
|
TxnWrite.Fragment fragment = modification.getTxnWriteFragment(idx++, state, options);
|
||||||
|
fragments.add(fragment);
|
||||||
|
}
|
||||||
|
return fragments;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RowIterator toCasResult(TxnData txnData)
|
||||||
|
{
|
||||||
|
FilteredPartition partition = txnData.get(TxnRead.SERIAL_READ);
|
||||||
|
return partition != null ? partition.rowIterator() : null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -177,7 +177,7 @@ public class DeleteStatement extends ModificationStatement
|
||||||
conditions,
|
conditions,
|
||||||
attrs);
|
attrs);
|
||||||
|
|
||||||
if (stmt.hasConditions() && !restrictions.hasAllPKColumnsRestrictedByEqualities())
|
if (stmt.hasConditions() && !restrictions.hasAllPrimaryKeyColumnsRestrictedByEqualities())
|
||||||
{
|
{
|
||||||
checkFalse(stmt.isVirtual(), "DELETE statements must restrict all PRIMARY KEY columns with equality relations");
|
checkFalse(stmt.isVirtual(), "DELETE statements must restrict all PRIMARY KEY columns with equality relations");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,15 +18,45 @@
|
||||||
package org.apache.cassandra.cql3.statements;
|
package org.apache.cassandra.cql3.statements;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.NavigableSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.collect.HashMultiset;
|
import com.google.common.collect.HashMultiset;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import org.apache.cassandra.auth.Permission;
|
import org.apache.cassandra.auth.Permission;
|
||||||
|
import org.apache.cassandra.cql3.Attributes;
|
||||||
|
import org.apache.cassandra.cql3.CQLStatement;
|
||||||
|
import org.apache.cassandra.cql3.ColumnIdentifier;
|
||||||
|
import org.apache.cassandra.cql3.ColumnSpecification;
|
||||||
|
import org.apache.cassandra.cql3.Ordering;
|
||||||
|
import org.apache.cassandra.cql3.terms.Constants;
|
||||||
|
import org.apache.cassandra.cql3.Operation;
|
||||||
|
import org.apache.cassandra.cql3.Operations;
|
||||||
|
import org.apache.cassandra.cql3.QualifiedName;
|
||||||
|
import org.apache.cassandra.cql3.QueryOptions;
|
||||||
|
import org.apache.cassandra.cql3.QueryProcessor;
|
||||||
|
import org.apache.cassandra.cql3.ResultSet;
|
||||||
|
import org.apache.cassandra.cql3.UpdateParameters;
|
||||||
|
import org.apache.cassandra.cql3.Validation;
|
||||||
|
import org.apache.cassandra.cql3.VariableSpecifications;
|
||||||
|
import org.apache.cassandra.cql3.WhereClause;
|
||||||
import org.apache.cassandra.cql3.constraints.ConstraintViolationException;
|
import org.apache.cassandra.cql3.constraints.ConstraintViolationException;
|
||||||
import org.apache.cassandra.db.guardrails.Guardrails;
|
import org.apache.cassandra.db.guardrails.Guardrails;
|
||||||
import org.apache.cassandra.dht.Token;
|
import org.apache.cassandra.dht.Token;
|
||||||
|
|
@ -37,7 +67,6 @@ import org.apache.cassandra.schema.Schema;
|
||||||
import org.apache.cassandra.schema.SchemaConstants;
|
import org.apache.cassandra.schema.SchemaConstants;
|
||||||
import org.apache.cassandra.schema.TableMetadata;
|
import org.apache.cassandra.schema.TableMetadata;
|
||||||
import org.apache.cassandra.schema.ViewMetadata;
|
import org.apache.cassandra.schema.ViewMetadata;
|
||||||
import org.apache.cassandra.cql3.*;
|
|
||||||
import org.apache.cassandra.cql3.conditions.ColumnCondition;
|
import org.apache.cassandra.cql3.conditions.ColumnCondition;
|
||||||
import org.apache.cassandra.cql3.conditions.ColumnConditions;
|
import org.apache.cassandra.cql3.conditions.ColumnConditions;
|
||||||
import org.apache.cassandra.cql3.conditions.Conditions;
|
import org.apache.cassandra.cql3.conditions.Conditions;
|
||||||
|
|
@ -46,6 +75,7 @@ import org.apache.cassandra.cql3.restrictions.StatementRestrictions;
|
||||||
import org.apache.cassandra.cql3.selection.ResultSetBuilder;
|
import org.apache.cassandra.cql3.selection.ResultSetBuilder;
|
||||||
import org.apache.cassandra.cql3.selection.Selection;
|
import org.apache.cassandra.cql3.selection.Selection;
|
||||||
import org.apache.cassandra.cql3.selection.Selection.Selectors;
|
import org.apache.cassandra.cql3.selection.Selection.Selectors;
|
||||||
|
import org.apache.cassandra.cql3.transactions.ReferenceOperation;
|
||||||
import org.apache.cassandra.db.*;
|
import org.apache.cassandra.db.*;
|
||||||
import org.apache.cassandra.db.filter.*;
|
import org.apache.cassandra.db.filter.*;
|
||||||
import org.apache.cassandra.db.marshal.BooleanType;
|
import org.apache.cassandra.db.marshal.BooleanType;
|
||||||
|
|
@ -62,8 +92,12 @@ import org.apache.cassandra.service.paxos.Ballot;
|
||||||
import org.apache.cassandra.service.paxos.BallotGenerator;
|
import org.apache.cassandra.service.paxos.BallotGenerator;
|
||||||
import org.apache.cassandra.service.paxos.Commit.Proposal;
|
import org.apache.cassandra.service.paxos.Commit.Proposal;
|
||||||
import org.apache.cassandra.transport.Dispatcher;
|
import org.apache.cassandra.transport.Dispatcher;
|
||||||
|
import org.apache.cassandra.service.accord.txn.TxnReferenceOperation;
|
||||||
|
import org.apache.cassandra.service.accord.txn.TxnReferenceOperations;
|
||||||
|
import org.apache.cassandra.service.accord.txn.TxnWrite;
|
||||||
import org.apache.cassandra.transport.messages.ResultMessage;
|
import org.apache.cassandra.transport.messages.ResultMessage;
|
||||||
import org.apache.cassandra.triggers.TriggerExecutor;
|
import org.apache.cassandra.triggers.TriggerExecutor;
|
||||||
|
import org.apache.cassandra.utils.ByteBufferUtil;
|
||||||
import org.apache.cassandra.utils.FBUtilities;
|
import org.apache.cassandra.utils.FBUtilities;
|
||||||
import org.apache.cassandra.utils.MD5Digest;
|
import org.apache.cassandra.utils.MD5Digest;
|
||||||
|
|
||||||
|
|
@ -146,6 +180,15 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa
|
||||||
requiresReadBuilder.add(operation.column);
|
requiresReadBuilder.add(operation.column);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (ReferenceOperation operation : operations.allSubstitutions())
|
||||||
|
{
|
||||||
|
ColumnMetadata receiver = operation.getReceiver();
|
||||||
|
updatedColumnsBuilder.add(receiver);
|
||||||
|
// If the operation requires a read-before-write, make sure its receiver is selected by the auto-read the
|
||||||
|
// transaction creates during update creation. (see createSelectForTxn())
|
||||||
|
if (operation.requiresRead())
|
||||||
|
requiresReadBuilder.add(receiver);
|
||||||
|
}
|
||||||
|
|
||||||
RegularAndStaticColumns modifiedColumns = updatedColumnsBuilder.build();
|
RegularAndStaticColumns modifiedColumns = updatedColumnsBuilder.build();
|
||||||
|
|
||||||
|
|
@ -389,6 +432,11 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa
|
||||||
return operations.staticOperations();
|
return operations.staticOperations();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Collection<ReferenceOperation> allReferenceOperations()
|
||||||
|
{
|
||||||
|
return operations.allSubstitutions();
|
||||||
|
}
|
||||||
|
|
||||||
public Iterable<ColumnMetadata> getColumnsWithConditions()
|
public Iterable<ColumnMetadata> getColumnsWithConditions()
|
||||||
{
|
{
|
||||||
return conditions.getColumns();
|
return conditions.getColumns();
|
||||||
|
|
@ -451,7 +499,7 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa
|
||||||
// * Deleting list element by value
|
// * Deleting list element by value
|
||||||
// * Performing addition on a StringType (i.e. concatenation, only supported for CAS operations)
|
// * Performing addition on a StringType (i.e. concatenation, only supported for CAS operations)
|
||||||
// * Performing addition on a NumberType, again only supported for CAS operations.
|
// * Performing addition on a NumberType, again only supported for CAS operations.
|
||||||
return !requiresRead.isEmpty();
|
return operations.requiresRead();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<DecoratedKey, Partition> readRequiredLists(Collection<ByteBuffer> partitionKeys,
|
private Map<DecoratedKey, Partition> readRequiredLists(Collection<ByteBuffer> partitionKeys,
|
||||||
|
|
@ -775,7 +823,7 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa
|
||||||
*
|
*
|
||||||
* @return list of the mutations
|
* @return list of the mutations
|
||||||
*/
|
*/
|
||||||
private List<? extends IMutation> getMutations(ClientState state,
|
public List<? extends IMutation> getMutations(ClientState state,
|
||||||
QueryOptions options,
|
QueryOptions options,
|
||||||
boolean local,
|
boolean local,
|
||||||
long timestamp,
|
long timestamp,
|
||||||
|
|
@ -797,6 +845,56 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public PartitionUpdate getTxnUpdate(ClientState state, QueryOptions options)
|
||||||
|
{
|
||||||
|
List<? extends IMutation> mutations = getMutations(state, options, false, 0, 0, new Dispatcher.RequestTime(0, 0));
|
||||||
|
if (mutations.size() != 1)
|
||||||
|
throw new IllegalArgumentException("When running withing a transaction, modification statements may only mutate a single partition");
|
||||||
|
return Iterables.getOnlyElement(mutations.get(0).getPartitionUpdates());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<TxnReferenceOperation> getTxnReferenceOps(List<ReferenceOperation> operations, QueryOptions options)
|
||||||
|
{
|
||||||
|
if (operations.isEmpty())
|
||||||
|
return Collections.emptyList();
|
||||||
|
|
||||||
|
List<TxnReferenceOperation> result = new ArrayList<>(operations.size());
|
||||||
|
for (ReferenceOperation operation : operations)
|
||||||
|
result.add(operation.bindAndGet(options));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TxnReferenceOperations getTxnReferenceOps(QueryOptions options, ClientState state)
|
||||||
|
{
|
||||||
|
List<TxnReferenceOperation> regularOps = getTxnReferenceOps(operations.regularSubstitutions(), options);
|
||||||
|
List<TxnReferenceOperation> staticOps = getTxnReferenceOps(operations.staticSubstitutions(), options);
|
||||||
|
Clustering<?> clustering = !regularOps.isEmpty() ? Iterables.getOnlyElement(createClustering(options, state)) : null;
|
||||||
|
return new TxnReferenceOperations(metadata, clustering, regularOps, staticOps);
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public void migrateReadRequiredOperations()
|
||||||
|
{
|
||||||
|
operations.migrateReadRequiredOperations();
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public List<ReferenceOperation> getSubstitutions()
|
||||||
|
{
|
||||||
|
return operations.allSubstitutions();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TxnWrite.Fragment getTxnWriteFragment(int index, ClientState state, QueryOptions options)
|
||||||
|
{
|
||||||
|
// When an Operation requires a read, this cannot be done right away and must be done by the transaction itself,
|
||||||
|
// so migrate those Operations to a ReferenceOperation (which works properly in this case).
|
||||||
|
operations.migrateReadRequiredOperations();
|
||||||
|
PartitionUpdate baseUpdate = getTxnUpdate(state, options);
|
||||||
|
TxnReferenceOperations referenceOps = getTxnReferenceOps(options, state);
|
||||||
|
return new TxnWrite.Fragment(index, baseUpdate, referenceOps);
|
||||||
|
}
|
||||||
|
|
||||||
final void addUpdates(UpdatesCollector collector,
|
final void addUpdates(UpdatesCollector collector,
|
||||||
List<ByteBuffer> keys,
|
List<ByteBuffer> keys,
|
||||||
ClientState state,
|
ClientState state,
|
||||||
|
|
@ -949,7 +1047,6 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa
|
||||||
requestTime);
|
requestTime);
|
||||||
|
|
||||||
return new UpdateParameters(metadata(),
|
return new UpdateParameters(metadata(),
|
||||||
updatedColumns(),
|
|
||||||
state,
|
state,
|
||||||
options,
|
options,
|
||||||
getTimestamp(timestamp, options),
|
getTimestamp(timestamp, options),
|
||||||
|
|
@ -995,6 +1092,7 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa
|
||||||
|
|
||||||
Conditions preparedConditions = prepareConditions(metadata, bindVariables);
|
Conditions preparedConditions = prepareConditions(metadata, bindVariables);
|
||||||
|
|
||||||
|
// TODO: if this is a txn and has a read name, and updates non-static columns, confirm it selects an entire row
|
||||||
return prepareInternal(state, metadata, bindVariables, preparedConditions, preparedAttributes);
|
return prepareInternal(state, metadata, bindVariables, preparedConditions, preparedAttributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1019,7 +1117,6 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa
|
||||||
if (ifNotExists)
|
if (ifNotExists)
|
||||||
{
|
{
|
||||||
assert conditions.isEmpty();
|
assert conditions.isEmpty();
|
||||||
assert !ifExists;
|
|
||||||
return Conditions.IF_NOT_EXISTS_CONDITION;
|
return Conditions.IF_NOT_EXISTS_CONDITION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1088,4 +1185,23 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa
|
||||||
return conditions;
|
return conditions;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final Constants.Value ONE = new Constants.Value(ByteBufferUtil.bytes(1));
|
||||||
|
|
||||||
|
public SelectStatement createSelectForTxn()
|
||||||
|
{
|
||||||
|
// TODO: get working with static-only updates that don't specify any/all primary key columns
|
||||||
|
Preconditions.checkState(getRestrictions().hasAllPrimaryKeyColumnsRestrictedByEqualities());
|
||||||
|
Selection selection = Selection.forColumns(metadata, Lists.newArrayList(requiresRead), false);
|
||||||
|
return new SelectStatement(metadata,
|
||||||
|
bindVariables,
|
||||||
|
SelectStatement.defaultParameters,
|
||||||
|
selection,
|
||||||
|
getRestrictions(),
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
ONE,
|
||||||
|
null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -78,4 +78,50 @@ public abstract class QualifiedStatement extends CQLStatement.Raw
|
||||||
{
|
{
|
||||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
|
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static abstract class Composite extends QualifiedStatement
|
||||||
|
{
|
||||||
|
Composite()
|
||||||
|
{
|
||||||
|
super(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract Iterable<? extends QualifiedStatement> getStatements();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFullyQualified()
|
||||||
|
{
|
||||||
|
for (QualifiedStatement statement : getStatements())
|
||||||
|
if (!statement.isFullyQualified())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setKeyspace(ClientState state)
|
||||||
|
{
|
||||||
|
for (QualifiedStatement statement : getStatements())
|
||||||
|
statement.setKeyspace(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setKeyspace(String keyspace)
|
||||||
|
{
|
||||||
|
for (QualifiedStatement statement : getStatements())
|
||||||
|
statement.setKeyspace(keyspace);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String keyspace()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String name()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,20 @@
|
||||||
package org.apache.cassandra.cql3.statements;
|
package org.apache.cassandra.cql3.statements;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
import java.util.stream.Collectors;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.NavigableSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeMap;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
|
@ -30,24 +40,26 @@ import com.google.common.base.Preconditions;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
|
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||||
|
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import org.apache.cassandra.audit.AuditLogContext;
|
import org.apache.cassandra.audit.AuditLogContext;
|
||||||
import org.apache.cassandra.audit.AuditLogEntryType;
|
import org.apache.cassandra.audit.AuditLogEntryType;
|
||||||
import org.apache.cassandra.auth.Permission;
|
import org.apache.cassandra.auth.Permission;
|
||||||
import org.apache.cassandra.cql3.restrictions.SingleRestriction;
|
import org.apache.cassandra.cql3.CQLStatement;
|
||||||
import org.apache.cassandra.cql3.terms.Term;
|
import org.apache.cassandra.cql3.ColumnIdentifier;
|
||||||
import org.apache.cassandra.db.guardrails.Guardrails;
|
import org.apache.cassandra.cql3.ColumnSpecification;
|
||||||
import org.apache.cassandra.index.Index;
|
import org.apache.cassandra.cql3.Ordering;
|
||||||
import org.apache.cassandra.schema.ColumnMetadata;
|
import org.apache.cassandra.cql3.QualifiedName;
|
||||||
import org.apache.cassandra.schema.Schema;
|
import org.apache.cassandra.cql3.QueryOptions;
|
||||||
import org.apache.cassandra.schema.SchemaConstants;
|
import org.apache.cassandra.cql3.QueryProcessor;
|
||||||
import org.apache.cassandra.schema.TableMetadata;
|
import org.apache.cassandra.cql3.ResultSet;
|
||||||
import org.apache.cassandra.schema.TableMetadataRef;
|
import org.apache.cassandra.cql3.VariableSpecifications;
|
||||||
import org.apache.cassandra.cql3.*;
|
import org.apache.cassandra.cql3.WhereClause;
|
||||||
import org.apache.cassandra.cql3.functions.Function;
|
import org.apache.cassandra.cql3.functions.Function;
|
||||||
|
import org.apache.cassandra.cql3.restrictions.SingleRestriction;
|
||||||
import org.apache.cassandra.cql3.restrictions.StatementRestrictions;
|
import org.apache.cassandra.cql3.restrictions.StatementRestrictions;
|
||||||
import org.apache.cassandra.cql3.selection.RawSelector;
|
import org.apache.cassandra.cql3.selection.RawSelector;
|
||||||
import org.apache.cassandra.cql3.selection.ResultSetBuilder;
|
import org.apache.cassandra.cql3.selection.ResultSetBuilder;
|
||||||
|
|
@ -56,10 +68,31 @@ import org.apache.cassandra.cql3.selection.Selectable.WithFunction;
|
||||||
import org.apache.cassandra.cql3.selection.Selection;
|
import org.apache.cassandra.cql3.selection.Selection;
|
||||||
import org.apache.cassandra.cql3.selection.Selection.Selectors;
|
import org.apache.cassandra.cql3.selection.Selection.Selectors;
|
||||||
import org.apache.cassandra.cql3.selection.Selector;
|
import org.apache.cassandra.cql3.selection.Selector;
|
||||||
import org.apache.cassandra.db.*;
|
import org.apache.cassandra.cql3.terms.Marker;
|
||||||
|
import org.apache.cassandra.cql3.terms.Term;
|
||||||
|
import org.apache.cassandra.db.Clustering;
|
||||||
|
import org.apache.cassandra.db.ColumnFamilyStore;
|
||||||
|
import org.apache.cassandra.db.ConsistencyLevel;
|
||||||
|
import org.apache.cassandra.db.DataRange;
|
||||||
|
import org.apache.cassandra.db.DecoratedKey;
|
||||||
|
import org.apache.cassandra.db.Keyspace;
|
||||||
|
import org.apache.cassandra.db.PartitionPosition;
|
||||||
|
import org.apache.cassandra.db.PartitionRangeReadQuery;
|
||||||
|
import org.apache.cassandra.db.ReadExecutionController;
|
||||||
|
import org.apache.cassandra.db.ReadQuery;
|
||||||
|
import org.apache.cassandra.db.SinglePartitionReadCommand;
|
||||||
|
import org.apache.cassandra.db.SinglePartitionReadQuery;
|
||||||
|
import org.apache.cassandra.db.Slice;
|
||||||
|
import org.apache.cassandra.db.Slices;
|
||||||
import org.apache.cassandra.db.aggregation.AggregationSpecification;
|
import org.apache.cassandra.db.aggregation.AggregationSpecification;
|
||||||
import org.apache.cassandra.db.aggregation.GroupMaker;
|
import org.apache.cassandra.db.aggregation.GroupMaker;
|
||||||
import org.apache.cassandra.db.filter.*;
|
import org.apache.cassandra.db.filter.ClusteringIndexFilter;
|
||||||
|
import org.apache.cassandra.db.filter.ClusteringIndexNamesFilter;
|
||||||
|
import org.apache.cassandra.db.filter.ClusteringIndexSliceFilter;
|
||||||
|
import org.apache.cassandra.db.filter.ColumnFilter;
|
||||||
|
import org.apache.cassandra.db.filter.DataLimits;
|
||||||
|
import org.apache.cassandra.db.filter.RowFilter;
|
||||||
|
import org.apache.cassandra.db.guardrails.Guardrails;
|
||||||
import org.apache.cassandra.db.marshal.CompositeType;
|
import org.apache.cassandra.db.marshal.CompositeType;
|
||||||
import org.apache.cassandra.db.marshal.Int32Type;
|
import org.apache.cassandra.db.marshal.Int32Type;
|
||||||
import org.apache.cassandra.db.partitions.PartitionIterator;
|
import org.apache.cassandra.db.partitions.PartitionIterator;
|
||||||
|
|
@ -67,9 +100,20 @@ import org.apache.cassandra.db.rows.Row;
|
||||||
import org.apache.cassandra.db.rows.RowIterator;
|
import org.apache.cassandra.db.rows.RowIterator;
|
||||||
import org.apache.cassandra.db.view.View;
|
import org.apache.cassandra.db.view.View;
|
||||||
import org.apache.cassandra.dht.AbstractBounds;
|
import org.apache.cassandra.dht.AbstractBounds;
|
||||||
import org.apache.cassandra.exceptions.*;
|
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||||
import org.apache.cassandra.metrics.ClientRequestSizeMetrics;
|
import org.apache.cassandra.exceptions.ReadSizeAbortException;
|
||||||
|
import org.apache.cassandra.exceptions.RequestExecutionException;
|
||||||
|
import org.apache.cassandra.exceptions.RequestFailureReason;
|
||||||
|
import org.apache.cassandra.exceptions.RequestValidationException;
|
||||||
|
import org.apache.cassandra.exceptions.UnauthorizedException;
|
||||||
|
import org.apache.cassandra.index.Index;
|
||||||
import org.apache.cassandra.index.IndexRegistry;
|
import org.apache.cassandra.index.IndexRegistry;
|
||||||
|
import org.apache.cassandra.metrics.ClientRequestSizeMetrics;
|
||||||
|
import org.apache.cassandra.schema.ColumnMetadata;
|
||||||
|
import org.apache.cassandra.schema.Schema;
|
||||||
|
import org.apache.cassandra.schema.SchemaConstants;
|
||||||
|
import org.apache.cassandra.schema.TableMetadata;
|
||||||
|
import org.apache.cassandra.schema.TableMetadataRef;
|
||||||
import org.apache.cassandra.serializers.MarshalException;
|
import org.apache.cassandra.serializers.MarshalException;
|
||||||
import org.apache.cassandra.service.ClientState;
|
import org.apache.cassandra.service.ClientState;
|
||||||
import org.apache.cassandra.service.ClientWarn;
|
import org.apache.cassandra.service.ClientWarn;
|
||||||
|
|
@ -85,9 +129,6 @@ import org.apache.cassandra.utils.ByteBufferUtil;
|
||||||
import org.apache.cassandra.utils.FBUtilities;
|
import org.apache.cassandra.utils.FBUtilities;
|
||||||
import org.apache.cassandra.utils.NoSpamLogger;
|
import org.apache.cassandra.utils.NoSpamLogger;
|
||||||
|
|
||||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
|
||||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
|
||||||
|
|
||||||
import static java.lang.String.format;
|
import static java.lang.String.format;
|
||||||
import static org.apache.cassandra.cql3.restrictions.StatementRestrictions.requiresAllowFilteringIfNotSpecified;
|
import static org.apache.cassandra.cql3.restrictions.StatementRestrictions.requiresAllowFilteringIfNotSpecified;
|
||||||
import static org.apache.cassandra.cql3.statements.RequestValidations.checkFalse;
|
import static org.apache.cassandra.cql3.statements.RequestValidations.checkFalse;
|
||||||
|
|
@ -109,7 +150,7 @@ import static org.apache.cassandra.utils.ByteBufferUtil.UNSET_BYTE_BUFFER;
|
||||||
* Note that select statements can be accessed by multiple threads, so we cannot rely on mutable attributes.
|
* Note that select statements can be accessed by multiple threads, so we cannot rely on mutable attributes.
|
||||||
*/
|
*/
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
public class SelectStatement implements CQLStatement.SingleKeyspaceCqlStatement
|
public class SelectStatement implements CQLStatement.SingleKeyspaceCqlStatement, CQLStatement.ReturningCQLStatement
|
||||||
{
|
{
|
||||||
private static final Logger logger = LoggerFactory.getLogger(SelectStatement.class);
|
private static final Logger logger = LoggerFactory.getLogger(SelectStatement.class);
|
||||||
private static final NoSpamLogger noSpamLogger = NoSpamLogger.getLogger(SelectStatement.logger, 1, TimeUnit.MINUTES);
|
private static final NoSpamLogger noSpamLogger = NoSpamLogger.getLogger(SelectStatement.logger, 1, TimeUnit.MINUTES);
|
||||||
|
|
@ -147,7 +188,7 @@ public class SelectStatement implements CQLStatement.SingleKeyspaceCqlStatement
|
||||||
private final ColumnComparator<List<ByteBuffer>> orderingComparator;
|
private final ColumnComparator<List<ByteBuffer>> orderingComparator;
|
||||||
|
|
||||||
// Used by forSelection below
|
// Used by forSelection below
|
||||||
private static final Parameters defaultParameters = new Parameters(Collections.emptyList(),
|
public static final Parameters defaultParameters = new Parameters(Collections.emptyList(),
|
||||||
Collections.emptyList(),
|
Collections.emptyList(),
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
|
@ -243,6 +284,7 @@ public class SelectStatement implements CQLStatement.SingleKeyspaceCqlStatement
|
||||||
null);
|
null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public ResultSet.ResultMetadata getResultMetadata()
|
public ResultSet.ResultMetadata getResultMetadata()
|
||||||
{
|
{
|
||||||
return selection.getResultMetadata();
|
return selection.getResultMetadata();
|
||||||
|
|
@ -911,6 +953,11 @@ public class SelectStatement implements CQLStatement.SingleKeyspaceCqlStatement
|
||||||
return getLimit(limit, options);
|
return getLimit(limit, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isLimitMarker()
|
||||||
|
{
|
||||||
|
return limit instanceof Marker;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the per partition limit specified by the user.
|
* Returns the per partition limit specified by the user.
|
||||||
* May be used by custom QueryHandler implementations
|
* May be used by custom QueryHandler implementations
|
||||||
|
|
@ -1186,10 +1233,28 @@ public class SelectStatement implements CQLStatement.SingleKeyspaceCqlStatement
|
||||||
{
|
{
|
||||||
// Cache locally for use by Guardrails
|
// Cache locally for use by Guardrails
|
||||||
this.state = state;
|
this.state = state;
|
||||||
return prepare(state, false);
|
return prepare(state, false, bindVariables);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SelectStatement prepare(ClientState state, boolean forView) throws InvalidRequestException
|
public SelectStatement prepare(ClientState state, boolean forView)
|
||||||
|
{
|
||||||
|
return prepare(state, forView, bindVariables);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SelectStatement prepare(VariableSpecifications variableSpecifications)
|
||||||
|
{
|
||||||
|
return prepare(state, false, variableSpecifications);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SelectStatement prepare(boolean forView)
|
||||||
|
{
|
||||||
|
return prepare(state, forView, bindVariables);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws InvalidRequestException if the statement being prepared is invalid
|
||||||
|
*/
|
||||||
|
public SelectStatement prepare(ClientState state, boolean forView, VariableSpecifications variableSpecifications) throws InvalidRequestException
|
||||||
{
|
{
|
||||||
TableMetadata table = Schema.instance.validateTable(keyspace(), name());
|
TableMetadata table = Schema.instance.validateTable(keyspace(), name());
|
||||||
|
|
||||||
|
|
@ -1197,7 +1262,7 @@ public class SelectStatement implements CQLStatement.SingleKeyspaceCqlStatement
|
||||||
boolean containsOnlyStaticColumns = selectOnlyStaticColumns(table, selectables);
|
boolean containsOnlyStaticColumns = selectOnlyStaticColumns(table, selectables);
|
||||||
|
|
||||||
List<Ordering> orderings = getOrderings(table);
|
List<Ordering> orderings = getOrderings(table);
|
||||||
StatementRestrictions restrictions = prepareRestrictions(state, table, bindVariables, orderings, containsOnlyStaticColumns, forView);
|
StatementRestrictions restrictions = prepareRestrictions(state, table, variableSpecifications, orderings, containsOnlyStaticColumns, forView);
|
||||||
|
|
||||||
// If we order post-query, the sorted column needs to be in the ResultSet for sorting,
|
// If we order post-query, the sorted column needs to be in the ResultSet for sorting,
|
||||||
// even if we don't ultimately ship them to the client (CASSANDRA-4911).
|
// even if we don't ultimately ship them to the client (CASSANDRA-4911).
|
||||||
|
|
@ -1206,7 +1271,7 @@ public class SelectStatement implements CQLStatement.SingleKeyspaceCqlStatement
|
||||||
|
|
||||||
Selection selection = prepareSelection(table,
|
Selection selection = prepareSelection(table,
|
||||||
selectables,
|
selectables,
|
||||||
bindVariables,
|
variableSpecifications,
|
||||||
resultSetOrderingColumns,
|
resultSetOrderingColumns,
|
||||||
restrictions);
|
restrictions);
|
||||||
|
|
||||||
|
|
@ -1242,15 +1307,15 @@ public class SelectStatement implements CQLStatement.SingleKeyspaceCqlStatement
|
||||||
checkNeedsFiltering(table, restrictions);
|
checkNeedsFiltering(table, restrictions);
|
||||||
|
|
||||||
return new SelectStatement(table,
|
return new SelectStatement(table,
|
||||||
bindVariables,
|
variableSpecifications,
|
||||||
parameters,
|
parameters,
|
||||||
selection,
|
selection,
|
||||||
restrictions,
|
restrictions,
|
||||||
isReversed,
|
isReversed,
|
||||||
aggregationSpecFactory,
|
aggregationSpecFactory,
|
||||||
orderingComparator,
|
orderingComparator,
|
||||||
prepareLimit(bindVariables, limit, keyspace(), limitReceiver()),
|
prepareLimit(variableSpecifications, limit, keyspace(), limitReceiver()),
|
||||||
prepareLimit(bindVariables, perPartitionLimit, keyspace(), perPartitionLimitReceiver()));
|
prepareLimit(variableSpecifications, perPartitionLimit, keyspace(), perPartitionLimitReceiver()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<ColumnMetadata> getResultSetOrdering(StatementRestrictions restrictions, Map<ColumnMetadata, Ordering> orderingColumns)
|
private Set<ColumnMetadata> getResultSetOrdering(StatementRestrictions restrictions, Map<ColumnMetadata, Ordering> orderingColumns)
|
||||||
|
|
@ -1620,18 +1685,30 @@ public class SelectStatement implements CQLStatement.SingleKeyspaceCqlStatement
|
||||||
public final boolean isDistinct;
|
public final boolean isDistinct;
|
||||||
public final boolean allowFiltering;
|
public final boolean allowFiltering;
|
||||||
public final boolean isJson;
|
public final boolean isJson;
|
||||||
|
public final String refName;
|
||||||
|
|
||||||
public Parameters(List<Ordering.Raw> orderings,
|
public Parameters(List<Ordering.Raw> orderings,
|
||||||
List<Selectable.Raw> groups,
|
List<Selectable.Raw> groups,
|
||||||
boolean isDistinct,
|
boolean isDistinct,
|
||||||
boolean allowFiltering,
|
boolean allowFiltering,
|
||||||
boolean isJson)
|
boolean isJson)
|
||||||
|
{
|
||||||
|
this(orderings, groups, isDistinct, allowFiltering, isJson, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Parameters(List<Ordering.Raw> orderings,
|
||||||
|
List<Selectable.Raw> groups,
|
||||||
|
boolean isDistinct,
|
||||||
|
boolean allowFiltering,
|
||||||
|
boolean isJson,
|
||||||
|
String refName)
|
||||||
{
|
{
|
||||||
this.orderings = orderings;
|
this.orderings = orderings;
|
||||||
this.groups = groups;
|
this.groups = groups;
|
||||||
this.isDistinct = isDistinct;
|
this.isDistinct = isDistinct;
|
||||||
this.allowFiltering = allowFiltering;
|
this.allowFiltering = allowFiltering;
|
||||||
this.isJson = isJson;
|
this.isJson = isJson;
|
||||||
|
this.refName = refName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1810,7 +1887,7 @@ public class SelectStatement implements CQLStatement.SingleKeyspaceCqlStatement
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String asCQL(QueryOptions options, ClientState state)
|
public String asCQL(QueryOptions options, ClientState state)
|
||||||
{
|
{
|
||||||
ColumnFilter columnFilter = selection.newSelectors(options).getColumnFilter();
|
ColumnFilter columnFilter = selection.newSelectors(options).getColumnFilter();
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,563 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.cassandra.cql3.statements;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.SortedSet;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import accord.api.Key;
|
||||||
|
import accord.primitives.Keys;
|
||||||
|
import accord.primitives.Txn;
|
||||||
|
import org.apache.cassandra.audit.AuditLogContext;
|
||||||
|
import org.apache.cassandra.audit.AuditLogEntryType;
|
||||||
|
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||||
|
import org.apache.cassandra.cql3.CQLStatement;
|
||||||
|
import org.apache.cassandra.cql3.ColumnSpecification;
|
||||||
|
import org.apache.cassandra.cql3.QueryOptions;
|
||||||
|
import org.apache.cassandra.cql3.ResultSet;
|
||||||
|
import org.apache.cassandra.cql3.VariableSpecifications;
|
||||||
|
import org.apache.cassandra.cql3.selection.ResultSetBuilder;
|
||||||
|
import org.apache.cassandra.cql3.selection.Selection;
|
||||||
|
import org.apache.cassandra.cql3.transactions.ConditionStatement;
|
||||||
|
import org.apache.cassandra.cql3.transactions.ReferenceOperation;
|
||||||
|
import org.apache.cassandra.cql3.transactions.RowDataReference;
|
||||||
|
import org.apache.cassandra.cql3.transactions.SelectReferenceSource;
|
||||||
|
import org.apache.cassandra.db.ReadQuery;
|
||||||
|
import org.apache.cassandra.db.SinglePartitionReadCommand;
|
||||||
|
import org.apache.cassandra.db.SinglePartitionReadQuery;
|
||||||
|
import org.apache.cassandra.db.marshal.AbstractType;
|
||||||
|
import org.apache.cassandra.db.partitions.FilteredPartition;
|
||||||
|
import org.apache.cassandra.schema.ColumnMetadata;
|
||||||
|
import org.apache.cassandra.service.ClientState;
|
||||||
|
import org.apache.cassandra.service.QueryState;
|
||||||
|
import org.apache.cassandra.service.accord.AccordService;
|
||||||
|
import org.apache.cassandra.service.accord.txn.TxnCondition;
|
||||||
|
import org.apache.cassandra.service.accord.txn.TxnData;
|
||||||
|
import org.apache.cassandra.service.accord.txn.TxnDataName;
|
||||||
|
import org.apache.cassandra.service.accord.txn.TxnNamedRead;
|
||||||
|
import org.apache.cassandra.service.accord.txn.TxnQuery;
|
||||||
|
import org.apache.cassandra.service.accord.txn.TxnRead;
|
||||||
|
import org.apache.cassandra.service.accord.txn.TxnReference;
|
||||||
|
import org.apache.cassandra.service.accord.txn.TxnUpdate;
|
||||||
|
import org.apache.cassandra.service.accord.txn.TxnWrite;
|
||||||
|
import org.apache.cassandra.transport.Dispatcher;
|
||||||
|
import org.apache.cassandra.transport.messages.ResultMessage;
|
||||||
|
import org.apache.cassandra.utils.FBUtilities;
|
||||||
|
import org.apache.cassandra.utils.LazyToString;
|
||||||
|
|
||||||
|
import static org.apache.cassandra.cql3.statements.RequestValidations.checkFalse;
|
||||||
|
import static org.apache.cassandra.cql3.statements.RequestValidations.checkNotNull;
|
||||||
|
import static org.apache.cassandra.cql3.statements.RequestValidations.checkTrue;
|
||||||
|
|
||||||
|
public class TransactionStatement implements CQLStatement.CompositeCQLStatement, CQLStatement.ReturningCQLStatement
|
||||||
|
{
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(TransactionStatement.class);
|
||||||
|
|
||||||
|
public static final String DUPLICATE_TUPLE_NAME_MESSAGE = "The name '%s' has already been used by a LET assignment.";
|
||||||
|
public static final String INCOMPLETE_PRIMARY_KEY_LET_MESSAGE = "SELECT in LET assignment must specify either all primary key elements or all partition key elements and LIMIT 1. In both cases partition key elements must be always specified with equality operators; CQL %s";
|
||||||
|
public static final String INCOMPLETE_PRIMARY_KEY_SELECT_MESSAGE = "Normal SELECT must specify either all primary key elements or all partition key elements and LIMIT 1. In both cases partition key elements must be always specified with equality operators; CQL %s";
|
||||||
|
public static final String NO_CONDITIONS_IN_UPDATES_MESSAGE = "Updates within transactions may not specify their own conditions.";
|
||||||
|
public static final String NO_TIMESTAMPS_IN_UPDATES_MESSAGE = "Updates within transactions may not specify custom timestamps.";
|
||||||
|
public static final String EMPTY_TRANSACTION_MESSAGE = "Transaction contains no reads or writes";
|
||||||
|
public static final String SELECT_REFS_NEED_COLUMN_MESSAGE = "SELECT references must specify a column.";
|
||||||
|
public static final String TRANSACTIONS_DISABLED_MESSAGE = "Accord transactions are disabled. (See accord_transactions_enabled in cassandra.yaml)";
|
||||||
|
public static final String ILLEGAL_RANGE_QUERY_MESSAGE = "Range queries are not allowed for reads within a transaction";
|
||||||
|
|
||||||
|
static class NamedSelect
|
||||||
|
{
|
||||||
|
final TxnDataName name;
|
||||||
|
final SelectStatement select;
|
||||||
|
|
||||||
|
public NamedSelect(TxnDataName name, SelectStatement select)
|
||||||
|
{
|
||||||
|
this.name = name;
|
||||||
|
this.select = select;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final List<NamedSelect> assignments;
|
||||||
|
private final NamedSelect returningSelect;
|
||||||
|
private final List<RowDataReference> returningReferences;
|
||||||
|
private final List<ModificationStatement> updates;
|
||||||
|
private final List<ConditionStatement> conditions;
|
||||||
|
|
||||||
|
private final VariableSpecifications bindVariables;
|
||||||
|
private final ResultSet.ResultMetadata resultMetadata;
|
||||||
|
|
||||||
|
public TransactionStatement(List<NamedSelect> assignments,
|
||||||
|
NamedSelect returningSelect,
|
||||||
|
List<RowDataReference> returningReferences,
|
||||||
|
List<ModificationStatement> updates,
|
||||||
|
List<ConditionStatement> conditions,
|
||||||
|
VariableSpecifications bindVariables)
|
||||||
|
{
|
||||||
|
this.assignments = assignments;
|
||||||
|
this.returningSelect = returningSelect;
|
||||||
|
this.returningReferences = returningReferences;
|
||||||
|
this.updates = updates;
|
||||||
|
this.conditions = conditions;
|
||||||
|
this.bindVariables = bindVariables;
|
||||||
|
|
||||||
|
if (returningSelect != null)
|
||||||
|
{
|
||||||
|
resultMetadata = returningSelect.select.getResultMetadata();
|
||||||
|
}
|
||||||
|
else if (returningReferences != null && !returningReferences.isEmpty())
|
||||||
|
{
|
||||||
|
List<ColumnSpecification> names = new ArrayList<>(returningReferences.size());
|
||||||
|
for (RowDataReference reference : returningReferences)
|
||||||
|
names.add(reference.toResultMetadata());
|
||||||
|
resultMetadata = new ResultSet.ResultMetadata(names);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
resultMetadata = ResultSet.ResultMetadata.EMPTY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ModificationStatement> getUpdates()
|
||||||
|
{
|
||||||
|
return updates;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ImmutableList<ColumnSpecification> getBindVariables()
|
||||||
|
{
|
||||||
|
return bindVariables.getImmutableBindVariables();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void authorize(ClientState state)
|
||||||
|
{
|
||||||
|
// Assess read permissions for all data from both explicit LET statements and generated reads.
|
||||||
|
for (NamedSelect let : assignments)
|
||||||
|
let.select.authorize(state);
|
||||||
|
|
||||||
|
if (returningSelect != null)
|
||||||
|
returningSelect.select.authorize(state);
|
||||||
|
|
||||||
|
for (ModificationStatement update : updates)
|
||||||
|
update.authorize(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validate(ClientState state)
|
||||||
|
{
|
||||||
|
for (NamedSelect statement : assignments)
|
||||||
|
statement.select.validate(state);
|
||||||
|
if (returningSelect != null)
|
||||||
|
returningSelect.select.validate(state);
|
||||||
|
for (ModificationStatement statement : updates)
|
||||||
|
statement.validate(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterable<CQLStatement> getStatements()
|
||||||
|
{
|
||||||
|
return () -> {
|
||||||
|
Stream<CQLStatement> stream = assignments.stream().map(n -> n.select);
|
||||||
|
if (returningSelect != null)
|
||||||
|
stream = Stream.concat(stream, Stream.of(returningSelect.select));
|
||||||
|
stream = Stream.concat(stream, updates.stream());
|
||||||
|
return stream.iterator();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResultSet.ResultMetadata getResultMetadata()
|
||||||
|
{
|
||||||
|
return resultMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
TxnNamedRead createNamedRead(NamedSelect namedSelect, QueryOptions options, ClientState state)
|
||||||
|
{
|
||||||
|
SelectStatement select = namedSelect.select;
|
||||||
|
ReadQuery readQuery = select.getQuery(options, 0);
|
||||||
|
checkTrue(readQuery instanceof SinglePartitionReadQuery.Group, ILLEGAL_RANGE_QUERY_MESSAGE, select.asCQL(options, state));
|
||||||
|
|
||||||
|
// We reject reads from both LET and SELECT that do not specify a single row.
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
SinglePartitionReadQuery.Group<SinglePartitionReadCommand> selectQuery = (SinglePartitionReadQuery.Group<SinglePartitionReadCommand>) readQuery;
|
||||||
|
|
||||||
|
if (selectQuery.queries.size() != 1)
|
||||||
|
throw new IllegalArgumentException("Within a transaction, SELECT statements must select a single partition; found " + selectQuery.queries.size() + " partitions");
|
||||||
|
|
||||||
|
return new TxnNamedRead(namedSelect.name, Iterables.getOnlyElement(selectQuery.queries));
|
||||||
|
}
|
||||||
|
|
||||||
|
List<TxnNamedRead> createNamedReads(NamedSelect namedSelect, QueryOptions options, ClientState state)
|
||||||
|
{
|
||||||
|
SelectStatement select = namedSelect.select;
|
||||||
|
ReadQuery readQuery = select.getQuery(options, 0);
|
||||||
|
checkTrue(readQuery instanceof SinglePartitionReadQuery.Group, ILLEGAL_RANGE_QUERY_MESSAGE, select.asCQL(options, state));
|
||||||
|
|
||||||
|
// We reject reads from both LET and SELECT that do not specify a single row.
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
SinglePartitionReadQuery.Group<SinglePartitionReadCommand> selectQuery = (SinglePartitionReadQuery.Group<SinglePartitionReadCommand>) readQuery;
|
||||||
|
|
||||||
|
if (selectQuery.queries.size() == 1)
|
||||||
|
return Collections.singletonList(new TxnNamedRead(namedSelect.name, Iterables.getOnlyElement(selectQuery.queries)));
|
||||||
|
|
||||||
|
List<TxnNamedRead> list = new ArrayList<>(selectQuery.queries.size());
|
||||||
|
for (int i = 0; i < selectQuery.queries.size(); i++)
|
||||||
|
list.add(new TxnNamedRead(TxnDataName.returning(i), selectQuery.queries.get(i)));
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<TxnNamedRead> createNamedReads(QueryOptions options, ClientState state, Map<TxnDataName, NamedSelect> autoReads, Consumer<Key> keyConsumer)
|
||||||
|
{
|
||||||
|
List<TxnNamedRead> reads = new ArrayList<>(assignments.size() + 1);
|
||||||
|
|
||||||
|
for (NamedSelect select : assignments)
|
||||||
|
{
|
||||||
|
TxnNamedRead read = createNamedRead(select, options, state);
|
||||||
|
keyConsumer.accept(read.key());
|
||||||
|
reads.add(read);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (returningSelect != null)
|
||||||
|
{
|
||||||
|
for (TxnNamedRead read : createNamedReads(returningSelect, options, state))
|
||||||
|
{
|
||||||
|
keyConsumer.accept(read.key());
|
||||||
|
reads.add(read);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (NamedSelect select : autoReads.values())
|
||||||
|
// don't need keyConsumer as the keys are known to exist due to Modification
|
||||||
|
reads.add(createNamedRead(select, options, state));
|
||||||
|
|
||||||
|
return reads;
|
||||||
|
}
|
||||||
|
|
||||||
|
TxnCondition createCondition(QueryOptions options)
|
||||||
|
{
|
||||||
|
if (conditions.isEmpty())
|
||||||
|
return TxnCondition.none();
|
||||||
|
if (conditions.size() == 1)
|
||||||
|
return conditions.get(0).createCondition(options);
|
||||||
|
|
||||||
|
List<TxnCondition> result = new ArrayList<>(conditions.size());
|
||||||
|
for (ConditionStatement condition : conditions)
|
||||||
|
result.add(condition.createCondition(options));
|
||||||
|
|
||||||
|
// TODO: OR support
|
||||||
|
return new TxnCondition.BooleanGroup(TxnCondition.Kind.AND, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<TxnWrite.Fragment> createWriteFragments(ClientState state, QueryOptions options, Map<TxnDataName, NamedSelect> autoReads, Consumer<Key> keyConsumer)
|
||||||
|
{
|
||||||
|
List<TxnWrite.Fragment> fragments = new ArrayList<>(updates.size());
|
||||||
|
int idx = 0;
|
||||||
|
for (ModificationStatement modification : updates)
|
||||||
|
{
|
||||||
|
TxnWrite.Fragment fragment = modification.getTxnWriteFragment(idx, state, options);
|
||||||
|
keyConsumer.accept(fragment.key);
|
||||||
|
fragments.add(fragment);
|
||||||
|
|
||||||
|
if (modification.allReferenceOperations().stream().anyMatch(ReferenceOperation::requiresRead))
|
||||||
|
{
|
||||||
|
// Reads are not merged by partition here due to potentially differing columns retrieved, etc.
|
||||||
|
TxnDataName partitionName = TxnDataName.partitionRead(modification.metadata(), fragment.key.partitionKey(), idx);
|
||||||
|
if (!autoReads.containsKey(partitionName))
|
||||||
|
autoReads.put(partitionName, new NamedSelect(partitionName, modification.createSelectForTxn()));
|
||||||
|
}
|
||||||
|
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
return fragments;
|
||||||
|
}
|
||||||
|
|
||||||
|
TxnUpdate createUpdate(ClientState state, QueryOptions options, Map<TxnDataName, NamedSelect> autoReads, Consumer<Key> keyConsumer)
|
||||||
|
{
|
||||||
|
return new TxnUpdate(createWriteFragments(state, options, autoReads, keyConsumer), createCondition(options));
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys toKeys(SortedSet<Key> keySet)
|
||||||
|
{
|
||||||
|
return new Keys(keySet);
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public Txn createTxn(ClientState state, QueryOptions options)
|
||||||
|
{
|
||||||
|
SortedSet<Key> keySet = new TreeSet<>();
|
||||||
|
|
||||||
|
if (updates.isEmpty())
|
||||||
|
{
|
||||||
|
// TODO: Test case around this...
|
||||||
|
Preconditions.checkState(conditions.isEmpty(), "No condition should exist without updates present");
|
||||||
|
List<TxnNamedRead> reads = createNamedReads(options, state, ImmutableMap.of(), keySet::add);
|
||||||
|
Keys txnKeys = toKeys(keySet);
|
||||||
|
TxnRead read = new TxnRead(reads, txnKeys);
|
||||||
|
return new Txn.InMemory(txnKeys, read, TxnQuery.ALL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Map<TxnDataName, NamedSelect> autoReads = new HashMap<>();
|
||||||
|
TxnUpdate update = createUpdate(state, options, autoReads, keySet::add);
|
||||||
|
List<TxnNamedRead> reads = createNamedReads(options, state, autoReads, keySet::add);
|
||||||
|
Keys txnKeys = toKeys(keySet);
|
||||||
|
TxnRead read = new TxnRead(reads, txnKeys);
|
||||||
|
return new Txn.InMemory(txnKeys, read, TxnQuery.ALL, update);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void checkAtMostOneRowSpecified(ClientState clientState, @Nullable QueryOptions options, SelectStatement select, String failureMessage)
|
||||||
|
{
|
||||||
|
if (select.getRestrictions().hasAllPrimaryKeyColumnsRestrictedByEqualities())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (options == null)
|
||||||
|
{
|
||||||
|
// If the limit is a non-terminal marker (because we're preparing), defer validation until execution.
|
||||||
|
if (select.isLimitMarker())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// The limit is already defined, so proceed with validation...
|
||||||
|
options = QueryOptions.DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
int limit = select.getLimit(options);
|
||||||
|
QueryOptions finalOptions = options; // javac thinks this is mutable so requires a copy
|
||||||
|
checkTrue(limit == 1 && select.getRestrictions().hasAllPartitionKeyColumnsRestrictedByEqualities(), failureMessage, LazyToString.lazy(() -> select.asCQL(finalOptions, clientState)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResultMessage execute(QueryState state, QueryOptions options, Dispatcher.RequestTime requestTime)
|
||||||
|
{
|
||||||
|
checkTrue(DatabaseDescriptor.getAccordTransactionsEnabled(), TRANSACTIONS_DISABLED_MESSAGE);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
for (NamedSelect assignment : assignments)
|
||||||
|
checkAtMostOneRowSpecified(state.getClientState(), options, assignment.select, INCOMPLETE_PRIMARY_KEY_LET_MESSAGE);
|
||||||
|
|
||||||
|
if (returningSelect != null)
|
||||||
|
checkAtMostOneRowSpecified(state.getClientState(), options, returningSelect.select, INCOMPLETE_PRIMARY_KEY_SELECT_MESSAGE);
|
||||||
|
|
||||||
|
TxnData data = AccordService.instance().coordinate(createTxn(state.getClientState(), options), options.getConsistency());
|
||||||
|
|
||||||
|
if (returningSelect != null)
|
||||||
|
{
|
||||||
|
ReadQuery readQuery = returningSelect.select.getQuery(options, 0);
|
||||||
|
checkTrue(readQuery instanceof SinglePartitionReadQuery.Group, ILLEGAL_RANGE_QUERY_MESSAGE, returningSelect.select.asCQL(options, state.getClientState()));
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
SinglePartitionReadQuery.Group<SinglePartitionReadCommand> selectQuery = (SinglePartitionReadQuery.Group<SinglePartitionReadCommand>) readQuery;
|
||||||
|
Selection.Selectors selectors = returningSelect.select.getSelection().newSelectors(options);
|
||||||
|
ResultSetBuilder result = new ResultSetBuilder(resultMetadata, selectors, false);
|
||||||
|
if (selectQuery.queries.size() == 1)
|
||||||
|
{
|
||||||
|
FilteredPartition partition = data.get(TxnDataName.returning());
|
||||||
|
if (partition != null)
|
||||||
|
returningSelect.select.processPartition(partition.rowIterator(), options, result, FBUtilities.nowInSeconds());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
long nowInSec = FBUtilities.nowInSeconds();
|
||||||
|
for (int i = 0; i < selectQuery.queries.size(); i++)
|
||||||
|
{
|
||||||
|
FilteredPartition partition = data.get(TxnDataName.returning(i));
|
||||||
|
if (partition != null)
|
||||||
|
returningSelect.select.processPartition(partition.rowIterator(), options, result, nowInSec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new ResultMessage.Rows(result.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (returningReferences != null)
|
||||||
|
{
|
||||||
|
List<AbstractType<?>> resultType = new ArrayList<>(returningReferences.size());
|
||||||
|
List<ColumnMetadata> columns = new ArrayList<>(returningReferences.size());
|
||||||
|
|
||||||
|
for (RowDataReference reference : returningReferences)
|
||||||
|
{
|
||||||
|
ColumnMetadata forMetadata = reference.toResultMetadata();
|
||||||
|
resultType.add(forMetadata.type);
|
||||||
|
columns.add(reference.column());
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultSetBuilder result = new ResultSetBuilder(resultMetadata, Selection.noopSelector(), false);
|
||||||
|
result.newRow(options.getProtocolVersion(), null, null, columns);
|
||||||
|
|
||||||
|
for (int i = 0; i < returningReferences.size(); i++)
|
||||||
|
{
|
||||||
|
RowDataReference reference = returningReferences.get(i);
|
||||||
|
TxnReference txnReference = reference.toTxnReference(options);
|
||||||
|
ByteBuffer buffer = txnReference.toByteBuffer(data, resultType.get(i));
|
||||||
|
result.add(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ResultMessage.Rows(result.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
// In the case of a write-only transaction, just return and empty result.
|
||||||
|
// TODO: This could be modified to return an indication of whether a condition (if present) succeeds.
|
||||||
|
return new ResultMessage.Void();
|
||||||
|
}
|
||||||
|
catch (Throwable t)
|
||||||
|
{
|
||||||
|
logger.error("Unexpected error with transaction", t);
|
||||||
|
throw t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResultMessage executeLocally(QueryState state, QueryOptions options)
|
||||||
|
{
|
||||||
|
return execute(state, options, Dispatcher.RequestTime.forImmediateExecution());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AuditLogContext getAuditLogContext()
|
||||||
|
{
|
||||||
|
return new AuditLogContext(AuditLogEntryType.TRANSACTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean eligibleAsPreparedStatement()
|
||||||
|
{
|
||||||
|
// false is the default, but still best to be explicit.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Parsed extends QualifiedStatement.Composite
|
||||||
|
{
|
||||||
|
private final List<SelectStatement.RawStatement> assignments;
|
||||||
|
private final SelectStatement.RawStatement select;
|
||||||
|
private final List<RowDataReference.Raw> returning;
|
||||||
|
private final List<ModificationStatement.Parsed> updates;
|
||||||
|
private final List<ConditionStatement.Raw> conditions;
|
||||||
|
private final List<RowDataReference.Raw> dataReferences;
|
||||||
|
|
||||||
|
public Parsed(List<SelectStatement.RawStatement> assignments,
|
||||||
|
SelectStatement.RawStatement select,
|
||||||
|
List<RowDataReference.Raw> returning,
|
||||||
|
List<ModificationStatement.Parsed> updates,
|
||||||
|
List<ConditionStatement.Raw> conditions,
|
||||||
|
List<RowDataReference.Raw> dataReferences)
|
||||||
|
{
|
||||||
|
this.assignments = assignments;
|
||||||
|
this.select = select;
|
||||||
|
this.returning = returning;
|
||||||
|
this.updates = updates;
|
||||||
|
this.conditions = conditions != null ? conditions : Collections.emptyList();
|
||||||
|
this.dataReferences = dataReferences;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Iterable<? extends QualifiedStatement> getStatements()
|
||||||
|
{
|
||||||
|
Iterable<QualifiedStatement> group = Iterables.concat(assignments, updates);
|
||||||
|
if (select != null)
|
||||||
|
group = Iterables.concat(group, Collections.singleton(select));
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CQLStatement prepare(ClientState state)
|
||||||
|
{
|
||||||
|
checkFalse(updates.isEmpty() && returning == null && select == null, EMPTY_TRANSACTION_MESSAGE);
|
||||||
|
|
||||||
|
if (select != null || returning != null)
|
||||||
|
checkTrue(select != null ^ returning != null, "Cannot specify both a full SELECT and a SELECT w/ LET references.");
|
||||||
|
|
||||||
|
List<NamedSelect> preparedAssignments = new ArrayList<>(assignments.size());
|
||||||
|
Map<TxnDataName, RowDataReference.ReferenceSource> refSources = new HashMap<>();
|
||||||
|
Set<TxnDataName> selectNames = new HashSet<>();
|
||||||
|
|
||||||
|
for (SelectStatement.RawStatement select : assignments)
|
||||||
|
{
|
||||||
|
checkNotNull(select.parameters.refName, "Assignments must be named");
|
||||||
|
TxnDataName name = TxnDataName.user(select.parameters.refName);
|
||||||
|
checkTrue(selectNames.add(name), DUPLICATE_TUPLE_NAME_MESSAGE, name.name());
|
||||||
|
|
||||||
|
SelectStatement prepared = select.prepare(bindVariables);
|
||||||
|
NamedSelect namedSelect = new NamedSelect(name, prepared);
|
||||||
|
checkAtMostOneRowSpecified(state, null, namedSelect.select, INCOMPLETE_PRIMARY_KEY_LET_MESSAGE);
|
||||||
|
preparedAssignments.add(namedSelect);
|
||||||
|
refSources.put(name, new SelectReferenceSource(prepared));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dataReferences != null)
|
||||||
|
for (RowDataReference.Raw reference : dataReferences)
|
||||||
|
reference.resolveReference(refSources);
|
||||||
|
|
||||||
|
NamedSelect returningSelect = null;
|
||||||
|
if (select != null)
|
||||||
|
{
|
||||||
|
returningSelect = new NamedSelect(TxnDataName.returning(), select.prepare(bindVariables));
|
||||||
|
checkAtMostOneRowSpecified(state, null, returningSelect.select, INCOMPLETE_PRIMARY_KEY_SELECT_MESSAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<RowDataReference> returningReferences = null;
|
||||||
|
|
||||||
|
if (returning != null)
|
||||||
|
{
|
||||||
|
// TODO: Eliminate/modify this check if we allow full tuple selections.
|
||||||
|
returningReferences = returning.stream().peek(raw -> checkTrue(raw.column() != null, SELECT_REFS_NEED_COLUMN_MESSAGE))
|
||||||
|
.map(RowDataReference.Raw::prepareAsReceiver)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ModificationStatement> preparedUpdates = new ArrayList<>(updates.size());
|
||||||
|
|
||||||
|
// check for any read-before-write updates
|
||||||
|
for (int i = 0; i < updates.size(); i++)
|
||||||
|
{
|
||||||
|
ModificationStatement.Parsed parsed = updates.get(i);
|
||||||
|
|
||||||
|
ModificationStatement prepared = parsed.prepare(state, bindVariables);
|
||||||
|
checkFalse(prepared.hasConditions(), NO_CONDITIONS_IN_UPDATES_MESSAGE);
|
||||||
|
checkFalse(prepared.isTimestampSet(), NO_TIMESTAMPS_IN_UPDATES_MESSAGE);
|
||||||
|
|
||||||
|
preparedUpdates.add(prepared);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ConditionStatement> preparedConditions = new ArrayList<>(conditions.size());
|
||||||
|
for (ConditionStatement.Raw condition : conditions)
|
||||||
|
// TODO: If we eventually support IF ks.function(ref) THEN, the keyspace will have to be provided here
|
||||||
|
preparedConditions.add(condition.prepare("[txn]", bindVariables));
|
||||||
|
|
||||||
|
return new TransactionStatement(preparedAssignments, returningSelect, returningReferences, preparedUpdates, preparedConditions, bindVariables);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -18,10 +18,13 @@
|
||||||
package org.apache.cassandra.cql3.statements;
|
package org.apache.cassandra.cql3.statements;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
|
||||||
import org.apache.cassandra.audit.AuditLogContext;
|
import org.apache.cassandra.audit.AuditLogContext;
|
||||||
import org.apache.cassandra.audit.AuditLogEntryType;
|
import org.apache.cassandra.audit.AuditLogEntryType;
|
||||||
import org.apache.cassandra.cql3.*;
|
import org.apache.cassandra.cql3.*;
|
||||||
|
|
@ -31,6 +34,8 @@ import org.apache.cassandra.cql3.constraints.ColumnConstraint;
|
||||||
import org.apache.cassandra.cql3.restrictions.StatementRestrictions;
|
import org.apache.cassandra.cql3.restrictions.StatementRestrictions;
|
||||||
import org.apache.cassandra.cql3.terms.Constants;
|
import org.apache.cassandra.cql3.terms.Constants;
|
||||||
import org.apache.cassandra.cql3.terms.Term;
|
import org.apache.cassandra.cql3.terms.Term;
|
||||||
|
import org.apache.cassandra.cql3.transactions.ReferenceOperation;
|
||||||
|
import org.apache.cassandra.cql3.transactions.ReferenceValue;
|
||||||
import org.apache.cassandra.db.Clustering;
|
import org.apache.cassandra.db.Clustering;
|
||||||
import org.apache.cassandra.db.Slice;
|
import org.apache.cassandra.db.Slice;
|
||||||
import org.apache.cassandra.db.partitions.PartitionUpdate;
|
import org.apache.cassandra.db.partitions.PartitionUpdate;
|
||||||
|
|
@ -39,6 +44,7 @@ import org.apache.cassandra.db.rows.Row;
|
||||||
import org.apache.cassandra.schema.ColumnMetadata;
|
import org.apache.cassandra.schema.ColumnMetadata;
|
||||||
import org.apache.cassandra.schema.TableMetadata;
|
import org.apache.cassandra.schema.TableMetadata;
|
||||||
import org.apache.cassandra.service.ClientState;
|
import org.apache.cassandra.service.ClientState;
|
||||||
|
import org.apache.cassandra.service.accord.txn.TxnReferenceOperation;
|
||||||
import org.apache.cassandra.utils.ByteBufferUtil;
|
import org.apache.cassandra.utils.ByteBufferUtil;
|
||||||
import org.apache.cassandra.utils.Pair;
|
import org.apache.cassandra.utils.Pair;
|
||||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||||
|
|
@ -53,6 +59,9 @@ import static org.apache.cassandra.cql3.statements.RequestValidations.checkFalse
|
||||||
*/
|
*/
|
||||||
public class UpdateStatement extends ModificationStatement
|
public class UpdateStatement extends ModificationStatement
|
||||||
{
|
{
|
||||||
|
public static final String UPDATING_PRIMARY_KEY_MESSAGE = "PRIMARY KEY part %s found in SET part";
|
||||||
|
public static final String CANNOT_SET_KEY_WITH_REFERENCE_MESSAGE = "Value reference %s cannot be used to insert PRIMARY KEY column %s";
|
||||||
|
|
||||||
private static final Constants.Value EMPTY = new Constants.Value(ByteBufferUtil.EMPTY_BYTE_BUFFER);
|
private static final Constants.Value EMPTY = new Constants.Value(ByteBufferUtil.EMPTY_BYTE_BUFFER);
|
||||||
|
|
||||||
private UpdateStatement(StatementType type,
|
private UpdateStatement(StatementType type,
|
||||||
|
|
@ -174,8 +183,16 @@ public class UpdateStatement extends ModificationStatement
|
||||||
|
|
||||||
if (def.isPrimaryKeyColumn())
|
if (def.isPrimaryKeyColumn())
|
||||||
{
|
{
|
||||||
|
checkFalse(value instanceof ReferenceValue.Raw, String.format(CANNOT_SET_KEY_WITH_REFERENCE_MESSAGE, value, def));
|
||||||
whereClause.add(Relation.singleColumn(columnNames.get(i), Operator.EQ, value));
|
whereClause.add(Relation.singleColumn(columnNames.get(i), Operator.EQ, value));
|
||||||
}
|
}
|
||||||
|
else if (value instanceof ReferenceValue.Raw)
|
||||||
|
{
|
||||||
|
ReferenceValue.Raw raw = (ReferenceValue.Raw) value;
|
||||||
|
ReferenceValue referenceValue = raw.prepare(def, bindVariables);
|
||||||
|
ReferenceOperation operation = new ReferenceOperation(def, TxnReferenceOperation.Kind.setterFor(def), null, null, referenceValue);
|
||||||
|
operations.add(def, operation);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Operation operation = new Operation.SetValue(value).prepare(metadata, def, !conditions.isEmpty());
|
Operation operation = new Operation.SetValue(value).prepare(metadata, def, !conditions.isEmpty());
|
||||||
|
|
@ -277,11 +294,59 @@ public class UpdateStatement extends ModificationStatement
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class OperationCollector
|
||||||
|
{
|
||||||
|
public final List<Pair<ColumnIdentifier, Operation.RawUpdate>> operations = new ArrayList<>();
|
||||||
|
public final List<Pair<ColumnIdentifier, ReferenceOperation.Raw>> referenceOps = new ArrayList<>();
|
||||||
|
|
||||||
|
public boolean conflictsWithExistingUpdate(ColumnIdentifier column, Operation.RawUpdate update)
|
||||||
|
{
|
||||||
|
for (Pair<ColumnIdentifier, Operation.RawUpdate> p : operations)
|
||||||
|
{
|
||||||
|
if (p.left.equals(column) && !p.right.isCompatibleWith(update))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean conflictsWithExistingSubstitution(ColumnIdentifier column)
|
||||||
|
{
|
||||||
|
for (Pair<ColumnIdentifier, ReferenceOperation.Raw> p : referenceOps)
|
||||||
|
{
|
||||||
|
if (p.left.equals(column))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addRawUpdate(ColumnIdentifier column, Operation.RawUpdate update)
|
||||||
|
{
|
||||||
|
operations.add(Pair.create(column, update));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean conflictsWithExistingUpdate(ColumnIdentifier column)
|
||||||
|
{
|
||||||
|
for (Pair<ColumnIdentifier, Operation.RawUpdate> p : operations)
|
||||||
|
{
|
||||||
|
if (p.left.equals(column))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addRawReferenceOperation(ColumnIdentifier column, ReferenceOperation.Raw substitution)
|
||||||
|
{
|
||||||
|
// TODO: Make sure there's more than a tuple name here...i.e. an actual reference column?
|
||||||
|
referenceOps.add(Pair.create(column, substitution));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static class ParsedUpdate extends ModificationStatement.Parsed
|
public static class ParsedUpdate extends ModificationStatement.Parsed
|
||||||
{
|
{
|
||||||
// Provided for an UPDATE
|
// Provided for an UPDATE
|
||||||
private final List<Pair<ColumnIdentifier, Operation.RawUpdate>> updates;
|
private final OperationCollector updates;
|
||||||
private final WhereClause whereClause;
|
private final WhereClause whereClause;
|
||||||
|
private final boolean isForTxn;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new UpdateStatement from a column family name, columns map, consistency
|
* Creates a new UpdateStatement from a column family name, columns map, consistency
|
||||||
|
|
@ -295,14 +360,16 @@ public class UpdateStatement extends ModificationStatement
|
||||||
* */
|
* */
|
||||||
public ParsedUpdate(QualifiedName name,
|
public ParsedUpdate(QualifiedName name,
|
||||||
Attributes.Raw attrs,
|
Attributes.Raw attrs,
|
||||||
List<Pair<ColumnIdentifier, Operation.RawUpdate>> updates,
|
OperationCollector updates,
|
||||||
WhereClause whereClause,
|
WhereClause whereClause,
|
||||||
List<ColumnCondition.Raw> conditions,
|
List<ColumnCondition.Raw> conditions,
|
||||||
boolean ifExists)
|
boolean ifExists,
|
||||||
|
boolean isForTxn)
|
||||||
{
|
{
|
||||||
super(name, StatementType.UPDATE, attrs, conditions, false, ifExists);
|
super(name, StatementType.UPDATE, attrs, conditions, false, ifExists);
|
||||||
this.updates = updates;
|
this.updates = updates;
|
||||||
this.whereClause = whereClause;
|
this.whereClause = whereClause;
|
||||||
|
this.isForTxn = isForTxn;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -314,17 +381,24 @@ public class UpdateStatement extends ModificationStatement
|
||||||
{
|
{
|
||||||
Operations operations = new Operations(type);
|
Operations operations = new Operations(type);
|
||||||
|
|
||||||
for (Pair<ColumnIdentifier, Operation.RawUpdate> entry : updates)
|
for (Pair<ColumnIdentifier, Operation.RawUpdate> entry : updates.operations)
|
||||||
{
|
{
|
||||||
ColumnMetadata def = metadata.getExistingColumn(entry.left);
|
ColumnMetadata def = metadata.getExistingColumn(entry.left);
|
||||||
|
checkFalse(def.isPrimaryKeyColumn(), UPDATING_PRIMARY_KEY_MESSAGE, def.name);
|
||||||
checkFalse(def.isPrimaryKeyColumn(), "PRIMARY KEY part %s found in SET part", def.name);
|
Operation operation = entry.right.prepare(metadata, def, !conditions.isEmpty() || isForTxn);
|
||||||
|
|
||||||
Operation operation = entry.right.prepare(metadata, def, !conditions.isEmpty());
|
|
||||||
operation.collectMarkerSpecification(bindVariables);
|
operation.collectMarkerSpecification(bindVariables);
|
||||||
operations.add(operation);
|
operations.add(operation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Preconditions.checkState(updates.referenceOps.isEmpty() || isForTxn);
|
||||||
|
for (Pair<ColumnIdentifier, ReferenceOperation.Raw> entry : updates.referenceOps)
|
||||||
|
{
|
||||||
|
ColumnMetadata def = metadata.getExistingColumn(entry.left);
|
||||||
|
checkFalse(def.isPrimaryKeyColumn(), UPDATING_PRIMARY_KEY_MESSAGE, def.name);
|
||||||
|
ReferenceOperation operation = entry.right.prepare(metadata, bindVariables);
|
||||||
|
operations.add(def, operation);
|
||||||
|
}
|
||||||
|
|
||||||
StatementRestrictions restrictions = newRestrictions(state,
|
StatementRestrictions restrictions = newRestrictions(state,
|
||||||
metadata,
|
metadata,
|
||||||
bindVariables,
|
bindVariables,
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ package org.apache.cassandra.cql3.terms;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
|
|
||||||
import org.apache.cassandra.cql3.AssignmentTestable;
|
import org.apache.cassandra.cql3.AssignmentTestable;
|
||||||
import org.apache.cassandra.cql3.CQL3Type;
|
import org.apache.cassandra.cql3.CQL3Type;
|
||||||
|
|
@ -30,7 +29,24 @@ import org.apache.cassandra.cql3.QueryOptions;
|
||||||
import org.apache.cassandra.cql3.UpdateParameters;
|
import org.apache.cassandra.cql3.UpdateParameters;
|
||||||
import org.apache.cassandra.db.Clustering;
|
import org.apache.cassandra.db.Clustering;
|
||||||
import org.apache.cassandra.db.DecoratedKey;
|
import org.apache.cassandra.db.DecoratedKey;
|
||||||
import org.apache.cassandra.db.marshal.*;
|
import org.apache.cassandra.db.marshal.AbstractType;
|
||||||
|
import org.apache.cassandra.db.marshal.AsciiType;
|
||||||
|
import org.apache.cassandra.db.marshal.BooleanType;
|
||||||
|
import org.apache.cassandra.db.marshal.ByteType;
|
||||||
|
import org.apache.cassandra.db.marshal.BytesType;
|
||||||
|
import org.apache.cassandra.db.marshal.CounterColumnType;
|
||||||
|
import org.apache.cassandra.db.marshal.DecimalType;
|
||||||
|
import org.apache.cassandra.db.marshal.DoubleType;
|
||||||
|
import org.apache.cassandra.db.marshal.DurationType;
|
||||||
|
import org.apache.cassandra.db.marshal.Int32Type;
|
||||||
|
import org.apache.cassandra.db.marshal.IntegerType;
|
||||||
|
import org.apache.cassandra.db.marshal.LongType;
|
||||||
|
import org.apache.cassandra.db.marshal.NumberType;
|
||||||
|
import org.apache.cassandra.db.marshal.ReversedType;
|
||||||
|
import org.apache.cassandra.db.marshal.StringType;
|
||||||
|
import org.apache.cassandra.db.marshal.TimeUUIDType;
|
||||||
|
import org.apache.cassandra.db.marshal.UTF8Type;
|
||||||
|
import org.apache.cassandra.db.marshal.UUIDType;
|
||||||
import org.apache.cassandra.db.rows.Cell;
|
import org.apache.cassandra.db.rows.Cell;
|
||||||
import org.apache.cassandra.db.rows.Row;
|
import org.apache.cassandra.db.rows.Row;
|
||||||
import org.apache.cassandra.exceptions.InvalidRequestException;
|
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||||
|
|
@ -39,12 +55,13 @@ import org.apache.cassandra.serializers.MarshalException;
|
||||||
import org.apache.cassandra.utils.ByteBufferUtil;
|
import org.apache.cassandra.utils.ByteBufferUtil;
|
||||||
import org.apache.cassandra.utils.FastByteOperations;
|
import org.apache.cassandra.utils.FastByteOperations;
|
||||||
|
|
||||||
|
import static java.nio.charset.StandardCharsets.US_ASCII;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Static helper methods and classes for constants.
|
* Static helper methods and classes for constants.
|
||||||
*/
|
*/
|
||||||
public abstract class Constants
|
public abstract class Constants
|
||||||
{
|
{
|
||||||
|
|
||||||
private static ByteBuffer getCurrentCellBuffer(ColumnMetadata column, DecoratedKey key, UpdateParameters params)
|
private static ByteBuffer getCurrentCellBuffer(ColumnMetadata column, DecoratedKey key, UpdateParameters params)
|
||||||
{
|
{
|
||||||
Row currentRow = params.getPrefetchedRow(key, column.isStatic() ? Clustering.STATIC_CLUSTERING : params.currentClustering());
|
Row currentRow = params.getPrefetchedRow(key, column.isStatic() ? Clustering.STATIC_CLUSTERING : params.currentClustering());
|
||||||
|
|
@ -59,7 +76,7 @@ public abstract class Constants
|
||||||
@Override
|
@Override
|
||||||
public AbstractType<?> getPreferedTypeFor(String text)
|
public AbstractType<?> getPreferedTypeFor(String text)
|
||||||
{
|
{
|
||||||
if (StandardCharsets.US_ASCII.newEncoder().canEncode(text))
|
if (US_ASCII.newEncoder().canEncode(text))
|
||||||
{
|
{
|
||||||
return AsciiType.instance;
|
return AsciiType.instance;
|
||||||
}
|
}
|
||||||
|
|
@ -272,6 +289,7 @@ public abstract class Constants
|
||||||
return new Literal(Type.DURATION, text);
|
return new Literal(Type.DURATION, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Value prepare(String keyspace, ColumnSpecification receiver) throws InvalidRequestException
|
public Value prepare(String keyspace, ColumnSpecification receiver) throws InvalidRequestException
|
||||||
{
|
{
|
||||||
if (!testAssignment(keyspace, receiver).isAssignable())
|
if (!testAssignment(keyspace, receiver).isAssignable())
|
||||||
|
|
|
||||||
|
|
@ -312,7 +312,7 @@ public abstract class Lists
|
||||||
|
|
||||||
public static class SetterByIndex extends Operation
|
public static class SetterByIndex extends Operation
|
||||||
{
|
{
|
||||||
private final Term idx;
|
public final Term idx;
|
||||||
|
|
||||||
public SetterByIndex(ColumnMetadata column, Term idx, Term t)
|
public SetterByIndex(ColumnMetadata column, Term idx, Term t)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -267,7 +267,7 @@ public final class Maps
|
||||||
|
|
||||||
public static class SetterByKey extends Operation
|
public static class SetterByKey extends Operation
|
||||||
{
|
{
|
||||||
private final Term k;
|
public final Term k;
|
||||||
|
|
||||||
public SetterByKey(ColumnMetadata column, Term k, Term t)
|
public SetterByKey(ColumnMetadata column, Term k, Term t)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -255,7 +255,7 @@ public final class UserTypes
|
||||||
|
|
||||||
public static class SetterByField extends Operation
|
public static class SetterByField extends Operation
|
||||||
{
|
{
|
||||||
private final FieldIdentifier field;
|
public final FieldIdentifier field;
|
||||||
|
|
||||||
public SetterByField(ColumnMetadata column, FieldIdentifier field, Term t)
|
public SetterByField(ColumnMetadata column, FieldIdentifier field, Term t)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,148 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.cassandra.cql3.transactions;
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
|
||||||
|
import org.apache.cassandra.cql3.ColumnSpecification;
|
||||||
|
import org.apache.cassandra.cql3.QueryOptions;
|
||||||
|
import org.apache.cassandra.cql3.terms.Term;
|
||||||
|
import org.apache.cassandra.cql3.VariableSpecifications;
|
||||||
|
import org.apache.cassandra.service.accord.txn.TxnCondition;
|
||||||
|
|
||||||
|
public class ConditionStatement
|
||||||
|
{
|
||||||
|
public enum Kind
|
||||||
|
{
|
||||||
|
IS_NOT_NULL(TxnCondition.Kind.IS_NOT_NULL, null),
|
||||||
|
IS_NULL(TxnCondition.Kind.IS_NULL, null),
|
||||||
|
EQ(TxnCondition.Kind.EQUAL, TxnCondition.Kind.EQUAL),
|
||||||
|
NEQ(TxnCondition.Kind.NOT_EQUAL, TxnCondition.Kind.NOT_EQUAL),
|
||||||
|
GT(TxnCondition.Kind.GREATER_THAN, TxnCondition.Kind.LESS_THAN),
|
||||||
|
GTE(TxnCondition.Kind.GREATER_THAN_OR_EQUAL, TxnCondition.Kind.LESS_THAN_OR_EQUAL),
|
||||||
|
LT(TxnCondition.Kind.LESS_THAN, TxnCondition.Kind.GREATER_THAN),
|
||||||
|
LTE(TxnCondition.Kind.LESS_THAN_OR_EQUAL, TxnCondition.Kind.GREATER_THAN_OR_EQUAL);
|
||||||
|
|
||||||
|
// TODO: Support for IN, CONTAINS, CONTAINS KEY
|
||||||
|
|
||||||
|
private final TxnCondition.Kind kind;
|
||||||
|
private final TxnCondition.Kind reversedKind;
|
||||||
|
|
||||||
|
Kind(TxnCondition.Kind kind, TxnCondition.Kind reversedKind)
|
||||||
|
{
|
||||||
|
this.kind = kind;
|
||||||
|
this.reversedKind = reversedKind;
|
||||||
|
}
|
||||||
|
|
||||||
|
TxnCondition.Kind toTxnKind(boolean reversed)
|
||||||
|
{
|
||||||
|
return reversed ? reversedKind : kind;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final RowDataReference reference;
|
||||||
|
private final Kind kind;
|
||||||
|
private final Term value;
|
||||||
|
private final boolean reversed;
|
||||||
|
|
||||||
|
public ConditionStatement(RowDataReference reference, Kind kind, Term value, boolean reversed)
|
||||||
|
{
|
||||||
|
this.reference = reference;
|
||||||
|
this.kind = kind;
|
||||||
|
this.value = value;
|
||||||
|
this.reversed = reversed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Raw
|
||||||
|
{
|
||||||
|
private final Term.Raw lhs;
|
||||||
|
private final Kind kind;
|
||||||
|
private final Term.Raw rhs;
|
||||||
|
|
||||||
|
public Raw(Term.Raw lhs, Kind kind, Term.Raw rhs)
|
||||||
|
{
|
||||||
|
Preconditions.checkArgument(lhs != null);
|
||||||
|
Preconditions.checkArgument((rhs == null) == (kind == Kind.IS_NOT_NULL || kind == Kind.IS_NULL));
|
||||||
|
this.lhs = lhs;
|
||||||
|
this.kind = kind;
|
||||||
|
this.rhs = rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConditionStatement prepare(String keyspace, VariableSpecifications bindVariables)
|
||||||
|
{
|
||||||
|
if (rhs == null)
|
||||||
|
{
|
||||||
|
// In the IS NULL/IS NOT NULL case, the reference will always be on the LHS
|
||||||
|
RowDataReference reference = ((RowDataReference.Raw) lhs).prepareAsReceiver();
|
||||||
|
reference.collectMarkerSpecification(bindVariables);
|
||||||
|
return new ConditionStatement(reference, kind, null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
RowDataReference reference;
|
||||||
|
Term value;
|
||||||
|
boolean reversed = false;
|
||||||
|
|
||||||
|
if (lhs instanceof RowDataReference.Raw)
|
||||||
|
{
|
||||||
|
reference = ((RowDataReference.Raw) lhs).prepareAsReceiver();
|
||||||
|
ColumnSpecification receiver = reference.getValueReceiver();
|
||||||
|
value = rhs.prepare(keyspace, receiver);
|
||||||
|
}
|
||||||
|
else if (rhs instanceof RowDataReference.Raw)
|
||||||
|
{
|
||||||
|
reference = ((RowDataReference.Raw) rhs).prepareAsReceiver();
|
||||||
|
ColumnSpecification receiver = reference.getValueReceiver();
|
||||||
|
value = lhs.prepare(keyspace, receiver);
|
||||||
|
// TxnCondition expects the reference to be on the LHS, so reverse the operator.
|
||||||
|
reversed = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new IllegalStateException("Either the left-hand or right-hand side must be a reference!");
|
||||||
|
}
|
||||||
|
|
||||||
|
reference.collectMarkerSpecification(bindVariables);
|
||||||
|
value.collectMarkerSpecification(bindVariables);
|
||||||
|
return new ConditionStatement(reference, kind, value, reversed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TxnCondition createCondition(QueryOptions options)
|
||||||
|
{
|
||||||
|
switch (kind)
|
||||||
|
{
|
||||||
|
case IS_NOT_NULL:
|
||||||
|
case IS_NULL:
|
||||||
|
return new TxnCondition.Exists(reference.toTxnReference(options), kind.toTxnKind(reversed));
|
||||||
|
case EQ:
|
||||||
|
case NEQ:
|
||||||
|
case GT:
|
||||||
|
case GTE:
|
||||||
|
case LT:
|
||||||
|
case LTE:
|
||||||
|
// TODO: Support for references on LHS and RHS
|
||||||
|
return new TxnCondition.Value(reference.toTxnReference(options),
|
||||||
|
kind.toTxnKind(reversed),
|
||||||
|
value.bindAndGet(options),
|
||||||
|
options.getProtocolVersion());
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,178 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.cassandra.cql3.transactions;
|
||||||
|
|
||||||
|
import org.apache.cassandra.cql3.ColumnIdentifier;
|
||||||
|
import org.apache.cassandra.cql3.FieldIdentifier;
|
||||||
|
import org.apache.cassandra.cql3.Operation;
|
||||||
|
import org.apache.cassandra.cql3.QueryOptions;
|
||||||
|
import org.apache.cassandra.cql3.VariableSpecifications;
|
||||||
|
import org.apache.cassandra.cql3.terms.Lists;
|
||||||
|
import org.apache.cassandra.cql3.terms.Maps;
|
||||||
|
import org.apache.cassandra.cql3.terms.Term;
|
||||||
|
import org.apache.cassandra.cql3.terms.UserTypes;
|
||||||
|
import org.apache.cassandra.db.marshal.AbstractType;
|
||||||
|
import org.apache.cassandra.db.marshal.CollectionType;
|
||||||
|
import org.apache.cassandra.db.marshal.MapType;
|
||||||
|
import org.apache.cassandra.db.marshal.SetType;
|
||||||
|
import org.apache.cassandra.db.marshal.UserType;
|
||||||
|
import org.apache.cassandra.db.rows.CellPath;
|
||||||
|
import org.apache.cassandra.schema.ColumnMetadata;
|
||||||
|
import org.apache.cassandra.schema.TableMetadata;
|
||||||
|
import org.apache.cassandra.service.accord.txn.TxnReferenceOperation;
|
||||||
|
import org.apache.cassandra.utils.ByteBufferUtil;
|
||||||
|
|
||||||
|
import static org.apache.cassandra.cql3.statements.RequestValidations.checkTrue;
|
||||||
|
import static org.apache.cassandra.db.marshal.CollectionType.Kind.MAP;
|
||||||
|
import static org.apache.cassandra.schema.TableMetadata.UNDEFINED_COLUMN_NAME_MESSAGE;
|
||||||
|
|
||||||
|
public class ReferenceOperation
|
||||||
|
{
|
||||||
|
private final ColumnMetadata receiver;
|
||||||
|
private final TxnReferenceOperation.Kind kind;
|
||||||
|
private final FieldIdentifier field;
|
||||||
|
private final Term key;
|
||||||
|
private final ReferenceValue value;
|
||||||
|
|
||||||
|
public ReferenceOperation(ColumnMetadata receiver, TxnReferenceOperation.Kind kind, Term key, FieldIdentifier field, ReferenceValue value)
|
||||||
|
{
|
||||||
|
this.receiver = receiver;
|
||||||
|
this.kind = kind;
|
||||||
|
this.key = key;
|
||||||
|
this.field = field;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link ReferenceOperation} from the given {@link Operation} for the purpose of defering execution
|
||||||
|
* within a transaction. When the language sees an Operation using a reference one is created already, but for cases
|
||||||
|
* that needs to defer execution (such as when {@link Operation#requiresRead()} is true), this method can be used.
|
||||||
|
*/
|
||||||
|
public static ReferenceOperation create(Operation operation)
|
||||||
|
{
|
||||||
|
TxnReferenceOperation.Kind kind = TxnReferenceOperation.Kind.from(operation);
|
||||||
|
ColumnMetadata receiver = operation.column;
|
||||||
|
|
||||||
|
// We already have a prepared reference value, so there is no need to inspect the value type.
|
||||||
|
ReferenceValue value = new ReferenceValue.Constant(operation.term());
|
||||||
|
Term key = extractKeyOrIndex(operation);
|
||||||
|
FieldIdentifier field = extractField(operation);
|
||||||
|
return new ReferenceOperation(receiver, kind, key, field, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TxnReferenceOperation.Kind getKind()
|
||||||
|
{
|
||||||
|
return kind;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReferenceValue getValue()
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ColumnMetadata getReceiver()
|
||||||
|
{
|
||||||
|
return receiver;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean requiresRead()
|
||||||
|
{
|
||||||
|
// TODO: Find a better way than delegating to the operation?
|
||||||
|
return kind.toOperation(receiver, null, null, null).requiresRead();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TxnReferenceOperation bindAndGet(QueryOptions options)
|
||||||
|
{
|
||||||
|
return new TxnReferenceOperation(kind,
|
||||||
|
receiver,
|
||||||
|
key != null ? key.bindAndGet(options) : null,
|
||||||
|
field != null ? field.bytes : null,
|
||||||
|
value.bindAndGet(options));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Raw
|
||||||
|
{
|
||||||
|
private final Operation.RawUpdate rawUpdate;
|
||||||
|
public final ColumnIdentifier column;
|
||||||
|
private final ReferenceValue.Raw value;
|
||||||
|
|
||||||
|
public Raw(Operation.RawUpdate rawUpdate, ColumnIdentifier column, ReferenceValue.Raw value)
|
||||||
|
{
|
||||||
|
this.rawUpdate = rawUpdate;
|
||||||
|
this.column = column;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReferenceOperation prepare(TableMetadata metadata, VariableSpecifications bindVariables)
|
||||||
|
{
|
||||||
|
ColumnMetadata receiver = metadata.getColumn(column);
|
||||||
|
Operation operation = rawUpdate.prepare(metadata, receiver, true);
|
||||||
|
TxnReferenceOperation.Kind kind = TxnReferenceOperation.Kind.from(operation);
|
||||||
|
Term key = extractKeyOrIndex(operation);
|
||||||
|
|
||||||
|
checkTrue(receiver != null, UNDEFINED_COLUMN_NAME_MESSAGE, column.toCQLString(), metadata);
|
||||||
|
AbstractType<?> type = receiver.type;
|
||||||
|
ColumnMetadata valueReceiver = receiver;
|
||||||
|
|
||||||
|
if (type.isCollection())
|
||||||
|
{
|
||||||
|
CollectionType<?> collectionType = (CollectionType<?>) type;
|
||||||
|
|
||||||
|
// The value for a map subtraction is actually a set (see Operation.Substraction)
|
||||||
|
if (kind == TxnReferenceOperation.Kind.SetDiscarder && collectionType.kind == MAP)
|
||||||
|
valueReceiver = valueReceiver.withNewType(SetType.getInstance(((MapType<?, ?>) type).getKeysType(), true));
|
||||||
|
|
||||||
|
if (kind == TxnReferenceOperation.Kind.ListSetterByIndex || kind == TxnReferenceOperation.Kind.MapSetterByKey)
|
||||||
|
valueReceiver = valueReceiver.withNewType(collectionType.valueComparator());
|
||||||
|
}
|
||||||
|
|
||||||
|
FieldIdentifier field = extractField(operation);
|
||||||
|
|
||||||
|
if (type.isUDT())
|
||||||
|
{
|
||||||
|
if (kind == TxnReferenceOperation.Kind.UserTypeSetterByField)
|
||||||
|
{
|
||||||
|
@SuppressWarnings("ConstantConditions") UserType userType = (UserType) type;
|
||||||
|
CellPath fieldPath = userType.cellPathForField(field);
|
||||||
|
int i = ByteBufferUtil.getUnsignedShort(fieldPath.get(0), 0);
|
||||||
|
valueReceiver = valueReceiver.withNewType(userType.fieldType(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ReferenceOperation(receiver, kind, key, field, value.prepare(valueReceiver, bindVariables));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static FieldIdentifier extractField(Operation operation)
|
||||||
|
{
|
||||||
|
if (operation instanceof UserTypes.SetterByField)
|
||||||
|
return ((UserTypes.SetterByField) operation).field;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Term extractKeyOrIndex(Operation operation)
|
||||||
|
{
|
||||||
|
// TODO: Is there a way to do this without exposing k and idx?
|
||||||
|
if (operation instanceof Maps.SetterByKey)
|
||||||
|
return ((Maps.SetterByKey) operation).k;
|
||||||
|
else if (operation instanceof Lists.SetterByIndex)
|
||||||
|
return ((Lists.SetterByIndex) operation).idx;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,155 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.cassandra.cql3.transactions;
|
||||||
|
|
||||||
|
import org.apache.cassandra.cql3.ColumnSpecification;
|
||||||
|
import org.apache.cassandra.cql3.QueryOptions;
|
||||||
|
import org.apache.cassandra.cql3.terms.Term;
|
||||||
|
import org.apache.cassandra.cql3.VariableSpecifications;
|
||||||
|
import org.apache.cassandra.db.marshal.AbstractType;
|
||||||
|
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||||
|
import org.apache.cassandra.schema.ColumnMetadata;
|
||||||
|
import org.apache.cassandra.service.accord.txn.TxnReferenceValue;
|
||||||
|
|
||||||
|
import static org.apache.cassandra.cql3.statements.RequestValidations.checkTrue;
|
||||||
|
|
||||||
|
public abstract class ReferenceValue
|
||||||
|
{
|
||||||
|
public abstract TxnReferenceValue bindAndGet(QueryOptions options);
|
||||||
|
|
||||||
|
public static abstract class Raw extends Term.Raw
|
||||||
|
{
|
||||||
|
public abstract ReferenceValue prepare(ColumnMetadata receiver, VariableSpecifications bindVariables);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Constant extends ReferenceValue
|
||||||
|
{
|
||||||
|
private final Term term;
|
||||||
|
|
||||||
|
public Constant(Term term)
|
||||||
|
{
|
||||||
|
this.term = term;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TxnReferenceValue bindAndGet(QueryOptions options)
|
||||||
|
{
|
||||||
|
return new TxnReferenceValue.Constant(term.bindAndGet(options));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Raw extends ReferenceValue.Raw
|
||||||
|
{
|
||||||
|
private final Term.Raw term;
|
||||||
|
|
||||||
|
public Raw(Term.Raw term)
|
||||||
|
{
|
||||||
|
this.term = term;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ReferenceValue prepare(ColumnMetadata receiver, VariableSpecifications bindVariables)
|
||||||
|
{
|
||||||
|
return new Constant(term.prepare(receiver.ksName, receiver));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TestResult testAssignment(String keyspace, ColumnSpecification receiver)
|
||||||
|
{
|
||||||
|
return term.testAssignment(keyspace, receiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Term prepare(String keyspace, ColumnSpecification receiver) throws InvalidRequestException
|
||||||
|
{
|
||||||
|
return term.prepare(keyspace, receiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getText()
|
||||||
|
{
|
||||||
|
return term.getText();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractType<?> getExactTypeIfKnown(String keyspace)
|
||||||
|
{
|
||||||
|
return term.getExactTypeIfKnown(keyspace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Substitution extends ReferenceValue
|
||||||
|
{
|
||||||
|
private final RowDataReference reference;
|
||||||
|
|
||||||
|
public Substitution(RowDataReference reference)
|
||||||
|
{
|
||||||
|
this.reference = reference;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TxnReferenceValue bindAndGet(QueryOptions options)
|
||||||
|
{
|
||||||
|
return new TxnReferenceValue.Substitution(reference.toTxnReference(options));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Raw extends ReferenceValue.Raw
|
||||||
|
{
|
||||||
|
private final RowDataReference.Raw reference;
|
||||||
|
|
||||||
|
public Raw(RowDataReference.Raw reference)
|
||||||
|
{
|
||||||
|
this.reference = reference;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ReferenceValue prepare(ColumnMetadata receiver, VariableSpecifications bindVariables)
|
||||||
|
{
|
||||||
|
reference.checkResolved();
|
||||||
|
checkTrue(reference.column() != null, "substitution references must reference a column (%s)", reference);
|
||||||
|
return new Substitution((RowDataReference) reference.prepare(null, receiver));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TestResult testAssignment(String keyspace, ColumnSpecification receiver)
|
||||||
|
{
|
||||||
|
return reference.testAssignment(keyspace, receiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Term prepare(String keyspace, ColumnSpecification receiver) throws InvalidRequestException
|
||||||
|
{
|
||||||
|
return reference.prepare(keyspace, receiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getText()
|
||||||
|
{
|
||||||
|
return reference.getText();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractType<?> getExactTypeIfKnown(String keyspace)
|
||||||
|
{
|
||||||
|
return reference.getExactTypeIfKnown(keyspace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,405 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.cassandra.cql3.transactions;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
|
||||||
|
import org.apache.cassandra.cql3.AssignmentTestable;
|
||||||
|
import org.apache.cassandra.cql3.ColumnIdentifier;
|
||||||
|
import org.apache.cassandra.cql3.ColumnSpecification;
|
||||||
|
import org.apache.cassandra.cql3.FieldIdentifier;
|
||||||
|
import org.apache.cassandra.cql3.terms.Lists;
|
||||||
|
import org.apache.cassandra.cql3.terms.Maps;
|
||||||
|
import org.apache.cassandra.cql3.QueryOptions;
|
||||||
|
import org.apache.cassandra.cql3.terms.Sets;
|
||||||
|
import org.apache.cassandra.cql3.terms.Term;
|
||||||
|
import org.apache.cassandra.cql3.terms.UserTypes;
|
||||||
|
import org.apache.cassandra.cql3.VariableSpecifications;
|
||||||
|
import org.apache.cassandra.cql3.functions.Function;
|
||||||
|
import org.apache.cassandra.cql3.functions.types.utils.Bytes;
|
||||||
|
import org.apache.cassandra.cql3.selection.Selectable;
|
||||||
|
import org.apache.cassandra.db.marshal.ListType;
|
||||||
|
import org.apache.cassandra.service.accord.txn.TxnDataName;
|
||||||
|
import org.apache.cassandra.db.marshal.AbstractType;
|
||||||
|
import org.apache.cassandra.db.marshal.CollectionType;
|
||||||
|
import org.apache.cassandra.db.marshal.MapType;
|
||||||
|
import org.apache.cassandra.db.marshal.SetType;
|
||||||
|
import org.apache.cassandra.db.marshal.UserType;
|
||||||
|
import org.apache.cassandra.db.rows.CellPath;
|
||||||
|
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||||
|
import org.apache.cassandra.schema.ColumnMetadata;
|
||||||
|
import org.apache.cassandra.service.accord.txn.TxnReference;
|
||||||
|
import org.apache.cassandra.utils.ByteBufferUtil;
|
||||||
|
|
||||||
|
import static org.apache.cassandra.cql3.statements.RequestValidations.checkNotNull;
|
||||||
|
|
||||||
|
public class RowDataReference extends Term.NonTerminal
|
||||||
|
{
|
||||||
|
public static final String CANNOT_FIND_TUPLE_MESSAGE = "Cannot resolve reference to tuple '%s'.";
|
||||||
|
public static final String COLUMN_NOT_IN_TUPLE_MESSAGE = "Column '%s' does not exist in tuple '%s'.";
|
||||||
|
|
||||||
|
private final TxnDataName selectName;
|
||||||
|
private final ColumnMetadata column;
|
||||||
|
private final Term elementPath;
|
||||||
|
private final CellPath fieldPath;
|
||||||
|
|
||||||
|
public RowDataReference(TxnDataName selectName, ColumnMetadata column, Term elementPath, CellPath fieldPath)
|
||||||
|
{
|
||||||
|
Preconditions.checkArgument(elementPath == null || fieldPath == null, "Cannot specify both element and field paths");
|
||||||
|
|
||||||
|
this.selectName = selectName;
|
||||||
|
this.column = column;
|
||||||
|
this.elementPath = elementPath;
|
||||||
|
this.fieldPath = fieldPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void collectMarkerSpecification(VariableSpecifications boundNames)
|
||||||
|
{
|
||||||
|
if (elementPath != null)
|
||||||
|
elementPath.collectMarkerSpecification(boundNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Terminal bind(QueryOptions options) throws InvalidRequestException
|
||||||
|
{
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsBindMarker()
|
||||||
|
{
|
||||||
|
return elementPath != null && elementPath.containsBindMarker();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addFunctionsTo(List<Function> functions)
|
||||||
|
{
|
||||||
|
throw new UnsupportedOperationException("Functions are not currently supported w/ reference terms.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public ColumnMetadata toResultMetadata()
|
||||||
|
{
|
||||||
|
ColumnIdentifier fullName = getFullyQualifiedName();
|
||||||
|
ColumnMetadata forMetadata = column.withNewName(fullName);
|
||||||
|
|
||||||
|
if (isElementSelection())
|
||||||
|
{
|
||||||
|
if (forMetadata.type instanceof ListType)
|
||||||
|
forMetadata = forMetadata.withNewType(((ListType<?>) forMetadata.type).valueComparator());
|
||||||
|
else if (forMetadata.type instanceof SetType)
|
||||||
|
forMetadata = forMetadata.withNewType(((SetType<?>) forMetadata.type).nameComparator());
|
||||||
|
else if (forMetadata.type instanceof MapType)
|
||||||
|
forMetadata = forMetadata.withNewType(((MapType<?, ?>) forMetadata.type).valueComparator());
|
||||||
|
}
|
||||||
|
else if (isFieldSelection())
|
||||||
|
{
|
||||||
|
forMetadata = forMetadata.withNewType(getFieldSelectionType());
|
||||||
|
}
|
||||||
|
return forMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ColumnSpecification getValueReceiver()
|
||||||
|
{
|
||||||
|
if (isElementSelection())
|
||||||
|
{
|
||||||
|
CollectionType.Kind collectionKind = ((CollectionType<?>) column.type).kind;
|
||||||
|
switch (collectionKind)
|
||||||
|
{
|
||||||
|
case LIST:
|
||||||
|
return Lists.valueSpecOf(column);
|
||||||
|
case MAP:
|
||||||
|
return Maps.valueSpecOf(column);
|
||||||
|
default:
|
||||||
|
throw new InvalidRequestException(String.format("Element selection not supported for column %s of type %s" ,
|
||||||
|
column.name, collectionKind));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (isFieldSelection())
|
||||||
|
{
|
||||||
|
return getFieldSelectionSpec();
|
||||||
|
}
|
||||||
|
|
||||||
|
return column;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isElementSelection()
|
||||||
|
{
|
||||||
|
return elementPath != null && column.type.isCollection();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFieldSelection()
|
||||||
|
{
|
||||||
|
return fieldPath != null && column.type.isUDT();
|
||||||
|
}
|
||||||
|
|
||||||
|
private AbstractType<?> getFieldSelectionType()
|
||||||
|
{
|
||||||
|
assert isFieldSelection() : "No field selection type exists";
|
||||||
|
return getFieldSelectionType(column, fieldPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AbstractType<?> getFieldSelectionType(ColumnMetadata column, CellPath fieldPath)
|
||||||
|
{
|
||||||
|
return ((UserType) column.type).fieldType(fieldPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ColumnSpecification getFieldSelectionSpec()
|
||||||
|
{
|
||||||
|
assert isFieldSelection() : "No field selection type exists";
|
||||||
|
int field = ByteBufferUtil.getUnsignedShort(fieldPath.get(0), 0);
|
||||||
|
return UserTypes.fieldSpecOf(column, field);
|
||||||
|
}
|
||||||
|
|
||||||
|
private CellPath bindCellPath(QueryOptions options)
|
||||||
|
{
|
||||||
|
if (fieldPath != null)
|
||||||
|
return fieldPath;
|
||||||
|
|
||||||
|
return elementPath != null ? CellPath.create(elementPath.bindAndGet(options)) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TxnReference toTxnReference(QueryOptions options)
|
||||||
|
{
|
||||||
|
Preconditions.checkState(elementPath == null || column.isComplex() || column.type.isFrozenCollection());
|
||||||
|
Preconditions.checkState(fieldPath == null || column.isComplex() || column.type.isUDT());
|
||||||
|
return new TxnReference(selectName, column, bindCellPath(options));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ColumnIdentifier getFullyQualifiedName()
|
||||||
|
{
|
||||||
|
// TODO: Make this more user-friendly...
|
||||||
|
String path = fieldPath != null ? '.' + Bytes.toHexString(fieldPath.get(0)) : (elementPath == null ? "" : "[0x" + elementPath + ']');
|
||||||
|
String fullName = selectName.name() + '.' + column.name.toString() + path;
|
||||||
|
return new ColumnIdentifier(fullName, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ColumnMetadata column()
|
||||||
|
{
|
||||||
|
return column;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Raw extends Term.Raw
|
||||||
|
{
|
||||||
|
private final Selectable.RawIdentifier tuple;
|
||||||
|
private final Selectable.RawIdentifier selected;
|
||||||
|
private final Object fieldOrElement;
|
||||||
|
|
||||||
|
private boolean isResolved = false;
|
||||||
|
|
||||||
|
private TxnDataName tupleName;
|
||||||
|
private ColumnMetadata column;
|
||||||
|
private Term elementPath = null;
|
||||||
|
private CellPath fieldPath = null;
|
||||||
|
|
||||||
|
public Raw(Selectable.RawIdentifier tuple, Selectable.Raw selected, Object fieldOrElement)
|
||||||
|
{
|
||||||
|
Preconditions.checkArgument(tuple != null, "tuple is null");
|
||||||
|
Preconditions.checkArgument(selected == null || selected instanceof Selectable.RawIdentifier, "selected is not a Selectable.RawIdentifier: " + selected);
|
||||||
|
this.tuple = tuple;
|
||||||
|
this.selected = (Selectable.RawIdentifier) selected;
|
||||||
|
this.fieldOrElement = fieldOrElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Raw fromSelectable(Selectable.RawIdentifier tuple, Selectable.Raw selectable)
|
||||||
|
{
|
||||||
|
if (selectable == null)
|
||||||
|
return new RowDataReference.Raw(tuple, null, null);
|
||||||
|
|
||||||
|
// TODO: Ideally it would be nice not to have to make items in the Selectables public
|
||||||
|
if (selectable instanceof Selectable.WithFieldSelection.Raw)
|
||||||
|
{
|
||||||
|
Selectable.WithFieldSelection.Raw selection = (Selectable.WithFieldSelection.Raw) selectable;
|
||||||
|
return new RowDataReference.Raw(tuple, selection.selected, selection.field);
|
||||||
|
}
|
||||||
|
else if (selectable instanceof Selectable.WithElementSelection.Raw)
|
||||||
|
{
|
||||||
|
Selectable.WithElementSelection.Raw elementSelection = (Selectable.WithElementSelection.Raw) selectable;
|
||||||
|
return new RowDataReference.Raw(tuple, elementSelection.selected, elementSelection.element);
|
||||||
|
}
|
||||||
|
else if (selectable instanceof Selectable.RawIdentifier)
|
||||||
|
{
|
||||||
|
Selectable.RawIdentifier selection = (Selectable.RawIdentifier) selectable;
|
||||||
|
return new RowDataReference.Raw(tuple, selection, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new UnsupportedOperationException("Cannot create column reference from selectable: " + selectable);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resolveFinished()
|
||||||
|
{
|
||||||
|
isResolved = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resolveReference(Map<TxnDataName, ReferenceSource> sources)
|
||||||
|
{
|
||||||
|
if (isResolved)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// root level name
|
||||||
|
tupleName = TxnDataName.user(tuple.toString());
|
||||||
|
ReferenceSource source = sources.get(tupleName);
|
||||||
|
checkNotNull(source, CANNOT_FIND_TUPLE_MESSAGE, tupleName.name());
|
||||||
|
|
||||||
|
if (selected == null)
|
||||||
|
{
|
||||||
|
resolveFinished();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
column = source.getColumn(selected.toString());
|
||||||
|
checkNotNull(column, COLUMN_NOT_IN_TUPLE_MESSAGE, selected.toString(), tupleName.name());
|
||||||
|
|
||||||
|
// TODO: confirm update partition key terms don't contain column references. This can't be done in prepare
|
||||||
|
// because there can be intermediate functions (ie: pk=row.v+1 or pk=_add(row.v, 5)). Need a recursive Term visitor
|
||||||
|
|
||||||
|
if (fieldOrElement == null)
|
||||||
|
{
|
||||||
|
resolveFinished();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (column.type.isCollection())
|
||||||
|
{
|
||||||
|
Term.Raw element = (Term.Raw) fieldOrElement;
|
||||||
|
elementPath = element.prepare(column.ksName, specForElementOrSlice(column));
|
||||||
|
}
|
||||||
|
else if (column.type.isUDT())
|
||||||
|
{
|
||||||
|
FieldIdentifier field = (FieldIdentifier) fieldOrElement;
|
||||||
|
UserType userType = (UserType) column.type;
|
||||||
|
fieldPath = userType.cellPathForField(field);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolveFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ColumnSpecification specForElementOrSlice(ColumnSpecification receiver)
|
||||||
|
{
|
||||||
|
switch (((CollectionType<?>) receiver.type).kind)
|
||||||
|
{
|
||||||
|
case LIST: return Lists.indexSpecOf(receiver);
|
||||||
|
case SET: return Sets.valueSpecOf(receiver);
|
||||||
|
case MAP: return Maps.keySpecOf(receiver);
|
||||||
|
default: throw new AssertionError("Unknown collection type: " + receiver.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkResolved()
|
||||||
|
{
|
||||||
|
if (!isResolved)
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AssignmentTestable.TestResult testAssignment(String keyspace, ColumnSpecification receiver)
|
||||||
|
{
|
||||||
|
checkResolved();
|
||||||
|
|
||||||
|
AbstractType<?> type = column.type;
|
||||||
|
|
||||||
|
if (elementPath != null)
|
||||||
|
{
|
||||||
|
CollectionType<?> collectionType = (CollectionType<?>) type;
|
||||||
|
type = collectionType.kind == CollectionType.Kind.SET ? collectionType.nameComparator() : collectionType.valueComparator();
|
||||||
|
}
|
||||||
|
else if (fieldPath != null)
|
||||||
|
{
|
||||||
|
type = RowDataReference.getFieldSelectionType(column, fieldPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return type.testAssignment(receiver.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Term prepare(String keyspace, ColumnSpecification receiver) throws InvalidRequestException
|
||||||
|
{
|
||||||
|
return prepare(keyspace, receiver, tupleName, column, elementPath, fieldPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RowDataReference prepareAsReceiver()
|
||||||
|
{
|
||||||
|
checkResolved();
|
||||||
|
return new RowDataReference(tupleName, column, elementPath, fieldPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private RowDataReference prepare(String keyspace,
|
||||||
|
ColumnSpecification receiver,
|
||||||
|
TxnDataName selectName,
|
||||||
|
ColumnMetadata column,
|
||||||
|
Term elementPath,
|
||||||
|
CellPath fieldPath)
|
||||||
|
{
|
||||||
|
if (!testAssignment(keyspace, receiver).isAssignable())
|
||||||
|
throw new InvalidRequestException(String.format("Invalid reference type %s (%s) for \"%s\" of type %s",
|
||||||
|
column.type, column.name, receiver.name, receiver.type.asCQL3Type()));
|
||||||
|
|
||||||
|
return new RowDataReference(selectName, column, elementPath, fieldPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getText()
|
||||||
|
{
|
||||||
|
StringBuilder text = new StringBuilder(tuple.toString());
|
||||||
|
|
||||||
|
if (selected != null)
|
||||||
|
text.append('.').append(selected);
|
||||||
|
|
||||||
|
if (fieldOrElement != null)
|
||||||
|
{
|
||||||
|
if (fieldOrElement instanceof Term.Raw)
|
||||||
|
{
|
||||||
|
Term.Raw element = (Term.Raw) fieldOrElement;
|
||||||
|
text.append('.').append(element.getText());
|
||||||
|
}
|
||||||
|
else if (fieldOrElement instanceof FieldIdentifier)
|
||||||
|
{
|
||||||
|
FieldIdentifier field = (FieldIdentifier) fieldOrElement;
|
||||||
|
text.append('.').append(field);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new IllegalStateException("Field or element is neither a raw term nor a field identifier");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return text.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractType<?> getExactTypeIfKnown(String keyspace)
|
||||||
|
{
|
||||||
|
checkResolved();
|
||||||
|
return column.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ColumnMetadata column()
|
||||||
|
{
|
||||||
|
return column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ReferenceSource
|
||||||
|
{
|
||||||
|
ColumnMetadata getColumn(String name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.cassandra.cql3.transactions;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.apache.cassandra.cql3.ColumnIdentifier;
|
||||||
|
import org.apache.cassandra.cql3.statements.SelectStatement;
|
||||||
|
import org.apache.cassandra.schema.ColumnMetadata;
|
||||||
|
|
||||||
|
import static org.apache.cassandra.cql3.statements.RequestValidations.checkTrue;
|
||||||
|
|
||||||
|
public class SelectReferenceSource implements RowDataReference.ReferenceSource
|
||||||
|
{
|
||||||
|
public static final String COLUMN_NOT_IN_SELECT_MESSAGE = "%s refererences a column not included in the select";
|
||||||
|
|
||||||
|
private final SelectStatement statement;
|
||||||
|
|
||||||
|
public SelectReferenceSource(SelectStatement statement)
|
||||||
|
{
|
||||||
|
this.statement = statement;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ColumnMetadata getColumn(String name)
|
||||||
|
{
|
||||||
|
ColumnMetadata column = statement.table.getColumn(new ColumnIdentifier(name, true));
|
||||||
|
if (column != null)
|
||||||
|
{
|
||||||
|
Set<ColumnMetadata> selectedColumns = new HashSet<>(statement.getSelection().getColumns());
|
||||||
|
checkTrue(selectedColumns.contains(column), COLUMN_NOT_IN_SELECT_MESSAGE, statement);
|
||||||
|
}
|
||||||
|
return column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -61,7 +61,7 @@ public class DeletionTime implements Comparable<DeletionTime>, IMeasurableMemory
|
||||||
|
|
||||||
// Do not use. This is a perf optimization where some data structures known to hold valid uints are allowed to use it.
|
// Do not use. This is a perf optimization where some data structures known to hold valid uints are allowed to use it.
|
||||||
// You should use 'build' instead to not workaround validations, corruption detections, etc
|
// You should use 'build' instead to not workaround validations, corruption detections, etc
|
||||||
static DeletionTime buildUnsafeWithUnsignedInteger(long markedForDeleteAt, int localDeletionTimeUnsignedInteger)
|
public static DeletionTime buildUnsafeWithUnsignedInteger(long markedForDeleteAt, int localDeletionTimeUnsignedInteger)
|
||||||
{
|
{
|
||||||
return CassandraUInt.compare(Cell.MAX_DELETION_TIME_UNSIGNED_INTEGER, localDeletionTimeUnsignedInteger) < 0
|
return CassandraUInt.compare(Cell.MAX_DELETION_TIME_UNSIGNED_INTEGER, localDeletionTimeUnsignedInteger) < 0
|
||||||
? new InvalidDeletionTime(markedForDeleteAt)
|
? new InvalidDeletionTime(markedForDeleteAt)
|
||||||
|
|
|
||||||
|
|
@ -230,6 +230,16 @@ public class MutableDeletionInfo implements DeletionInfo
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DeletionInfo updateAllTimestampAndLocalDeletionTime(long timestamp, int localDeletionTime)
|
||||||
|
{
|
||||||
|
if (partitionDeletion.markedForDeleteAt() != Long.MIN_VALUE)
|
||||||
|
partitionDeletion = DeletionTime.buildUnsafeWithUnsignedInteger(timestamp, localDeletionTime);
|
||||||
|
|
||||||
|
if (ranges != null)
|
||||||
|
ranges.updateAllTimestampAndLocalDeletionTime(timestamp, localDeletionTime);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o)
|
public boolean equals(Object o)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ import org.apache.cassandra.utils.bytecomparable.ByteSource;
|
||||||
|
|
||||||
public interface PartitionPosition extends RingPosition<PartitionPosition>, ByteComparable
|
public interface PartitionPosition extends RingPosition<PartitionPosition>, ByteComparable
|
||||||
{
|
{
|
||||||
public static enum Kind
|
public enum Kind
|
||||||
{
|
{
|
||||||
// Only add new values to the end of the enum, the ordinal is used
|
// Only add new values to the end of the enum, the ordinal is used
|
||||||
// during serialization
|
// during serialization
|
||||||
|
|
|
||||||
|
|
@ -328,6 +328,15 @@ public class RangeTombstoneList implements Iterable<RangeTombstone>, IMeasurable
|
||||||
markedAts[i] = timestamp;
|
markedAts[i] = timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateAllTimestampAndLocalDeletionTime(long timestamp, int localDeletionTime)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < size; i++)
|
||||||
|
{
|
||||||
|
markedAts[i] = timestamp;
|
||||||
|
delTimesUnsignedIntegers[i] = localDeletionTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private RangeTombstone rangeTombstone(int idx)
|
private RangeTombstone rangeTombstone(int idx)
|
||||||
{
|
{
|
||||||
return new RangeTombstone(Slice.make(starts[idx], ends[idx]), DeletionTime.buildUnsafeWithUnsignedInteger(markedAts[idx], delTimesUnsignedIntegers[idx]));
|
return new RangeTombstone(Slice.make(starts[idx], ends[idx]), DeletionTime.buildUnsafeWithUnsignedInteger(markedAts[idx], delTimesUnsignedIntegers[idx]));
|
||||||
|
|
|
||||||
|
|
@ -434,6 +434,24 @@ public class SinglePartitionReadCommand extends ReadCommand implements SinglePar
|
||||||
isTrackingWarnings());
|
isTrackingWarnings());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SinglePartitionReadCommand withNowInSec(int nowInSec)
|
||||||
|
{
|
||||||
|
return new SinglePartitionReadCommand(serializedAtEpoch(),
|
||||||
|
isDigestQuery(),
|
||||||
|
digestVersion(),
|
||||||
|
acceptsTransient(),
|
||||||
|
metadata(),
|
||||||
|
nowInSec,
|
||||||
|
columnFilter(),
|
||||||
|
rowFilter(),
|
||||||
|
limits(),
|
||||||
|
partitionKey(),
|
||||||
|
clusteringIndexFilter(),
|
||||||
|
indexQueryPlan(),
|
||||||
|
isTrackingWarnings(),
|
||||||
|
dataRange());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DecoratedKey partitionKey()
|
public DecoratedKey partitionKey()
|
||||||
{
|
{
|
||||||
|
|
@ -492,7 +510,9 @@ public class SinglePartitionReadCommand extends ReadCommand implements SinglePar
|
||||||
metric.readLatency.addNano(latencyNanos);
|
metric.readLatency.addNano(latencyNanos);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected UnfilteredPartitionIterator queryStorage(final ColumnFamilyStore cfs, ReadExecutionController executionController)
|
@VisibleForTesting
|
||||||
|
@SuppressWarnings("resource") // we close the created iterator through closing the result of this method (and SingletonUnfilteredPartitionIterator ctor cannot fail)
|
||||||
|
public UnfilteredPartitionIterator queryStorage(final ColumnFamilyStore cfs, ReadExecutionController executionController)
|
||||||
{
|
{
|
||||||
// skip the row cache and go directly to sstables/memtable if repaired status of
|
// skip the row cache and go directly to sstables/memtable if repaired status of
|
||||||
// data is being tracked. This is only requested after an initial digest mismatch
|
// data is being tracked. This is only requested after an initial digest mismatch
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,7 @@ import org.apache.cassandra.gms.VersionedValue;
|
||||||
import org.apache.cassandra.io.sstable.SSTableId;
|
import org.apache.cassandra.io.sstable.SSTableId;
|
||||||
import org.apache.cassandra.io.sstable.SequenceBasedSSTableId;
|
import org.apache.cassandra.io.sstable.SequenceBasedSSTableId;
|
||||||
import org.apache.cassandra.io.util.DataInputBuffer;
|
import org.apache.cassandra.io.util.DataInputBuffer;
|
||||||
|
import org.apache.cassandra.io.util.DataInputPlus;
|
||||||
import org.apache.cassandra.io.util.DataOutputBuffer;
|
import org.apache.cassandra.io.util.DataOutputBuffer;
|
||||||
import org.apache.cassandra.io.util.File;
|
import org.apache.cassandra.io.util.File;
|
||||||
import org.apache.cassandra.io.util.RebufferingInputStream;
|
import org.apache.cassandra.io.util.RebufferingInputStream;
|
||||||
|
|
@ -1855,9 +1856,9 @@ public final class SystemKeyspace
|
||||||
if (!previous.equals(NULL_VERSION.toString()) && !previous.equals(next))
|
if (!previous.equals(NULL_VERSION.toString()) && !previous.equals(next))
|
||||||
{
|
{
|
||||||
List<String> entities = new ArrayList<>();
|
List<String> entities = new ArrayList<>();
|
||||||
for (String keyspace : SchemaConstants.LOCAL_SYSTEM_KEYSPACE_NAMES)
|
for (Keyspace keyspace : Keyspace.system())
|
||||||
{
|
{
|
||||||
for (ColumnFamilyStore cfs : Keyspace.open(keyspace).getColumnFamilyStores())
|
for (ColumnFamilyStore cfs : keyspace.getColumnFamilyStores())
|
||||||
entities.add(cfs.getKeyspaceTableName());
|
entities.add(cfs.getKeyspaceTableName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1934,12 +1935,10 @@ public final class SystemKeyspace
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private static Range<Token> byteBufferToRange(ByteBuffer rawRange, IPartitioner partitioner)
|
private static Range<Token> byteBufferToRange(ByteBuffer rawRange, IPartitioner partitioner)
|
||||||
{
|
{
|
||||||
try
|
try (DataInputPlus.DataInputStreamPlus in = new DataInputBuffer(ByteBufferUtil.getArray(rawRange)))
|
||||||
{
|
{
|
||||||
// See rangeToBytes above for why version is 0.
|
// See rangeToBytes above for why version is 0.
|
||||||
return (Range<Token>) Range.tokenSerializer.deserialize(new DataInputBuffer(ByteBufferUtil.getArray(rawRange)),
|
return (Range<Token>) Range.tokenSerializer.deserialize(in, partitioner, 0);
|
||||||
partitioner,
|
|
||||||
0);
|
|
||||||
}
|
}
|
||||||
catch (IOException e)
|
catch (IOException e)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,10 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.cassandra.db;
|
package org.apache.cassandra.db;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identifier for what type of operation timed out. This type is driver facing as a String, but some drivers convert
|
||||||
|
* this to an enum, meaning any changes to this type require protocol changes and driver support.
|
||||||
|
*/
|
||||||
public enum WriteType
|
public enum WriteType
|
||||||
{
|
{
|
||||||
SIMPLE,
|
SIMPLE,
|
||||||
|
|
@ -26,5 +30,6 @@ public enum WriteType
|
||||||
BATCH_LOG,
|
BATCH_LOG,
|
||||||
CAS,
|
CAS,
|
||||||
VIEW,
|
VIEW,
|
||||||
CDC;
|
CDC
|
||||||
|
//TODO update client protocol to support "TRANSACTION"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -197,20 +197,6 @@ public class ClusteringIndexNamesFilter extends AbstractClusteringIndexFilter
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean equals(Object o)
|
|
||||||
{
|
|
||||||
if (this == o) return true;
|
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
|
||||||
ClusteringIndexNamesFilter that = (ClusteringIndexNamesFilter) o;
|
|
||||||
return Objects.equals(clusterings, that.clusterings) &&
|
|
||||||
Objects.equals(reversed, that.reversed);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int hashCode()
|
|
||||||
{
|
|
||||||
return Objects.hash(clusterings, reversed);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Kind kind()
|
public Kind kind()
|
||||||
{
|
{
|
||||||
return Kind.NAMES;
|
return Kind.NAMES;
|
||||||
|
|
|
||||||
|
|
@ -682,7 +682,7 @@ public abstract class ColumnFilter
|
||||||
SortedSetMultimap<ColumnIdentifier, ColumnSubselection> subSelections)
|
SortedSetMultimap<ColumnIdentifier, ColumnSubselection> subSelections)
|
||||||
{
|
{
|
||||||
assert queried != null;
|
assert queried != null;
|
||||||
assert fetched.includes(queried);
|
assert fetched.includes(queried) : String.format("Queries columns %s are not included in the fetch strategy %s", queried, fetched);
|
||||||
|
|
||||||
this.fetchingStrategy = fetchingStrategy;
|
this.fetchingStrategy = fetchingStrategy;
|
||||||
this.queried = queried;
|
this.queried = queried;
|
||||||
|
|
|
||||||
|
|
@ -440,7 +440,7 @@ public abstract class AbstractType<T> implements Comparator<ByteBuffer>, Assignm
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AbstractType<?> freeze()
|
public AbstractType<T> freeze()
|
||||||
{
|
{
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import java.nio.charset.Charset;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import accord.utils.Invariants;
|
||||||
import org.apache.cassandra.db.Digest;
|
import org.apache.cassandra.db.Digest;
|
||||||
import org.apache.cassandra.db.TypeSizes;
|
import org.apache.cassandra.db.TypeSizes;
|
||||||
import org.apache.cassandra.io.util.DataInputPlus;
|
import org.apache.cassandra.io.util.DataInputPlus;
|
||||||
|
|
@ -107,6 +108,7 @@ public class ByteArrayAccessor implements ValueAccessor<byte[]>
|
||||||
@Override
|
@Override
|
||||||
public byte[] slice(byte[] input, int offset, int length)
|
public byte[] slice(byte[] input, int offset, int length)
|
||||||
{
|
{
|
||||||
|
Invariants.checkArgument(offset + length <= input.length);
|
||||||
return Arrays.copyOfRange(input, offset, offset + length);
|
return Arrays.copyOfRange(input, offset, offset + length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,9 +37,9 @@ import org.apache.cassandra.exceptions.SyntaxException;
|
||||||
import org.apache.cassandra.serializers.ListSerializer;
|
import org.apache.cassandra.serializers.ListSerializer;
|
||||||
import org.apache.cassandra.serializers.MarshalException;
|
import org.apache.cassandra.serializers.MarshalException;
|
||||||
import org.apache.cassandra.utils.ByteBufferUtil;
|
import org.apache.cassandra.utils.ByteBufferUtil;
|
||||||
|
import org.apache.cassandra.transport.ProtocolVersion;
|
||||||
import org.apache.cassandra.utils.JsonUtils;
|
import org.apache.cassandra.utils.JsonUtils;
|
||||||
import org.apache.cassandra.utils.TimeUUID;
|
import org.apache.cassandra.utils.TimeUUID;
|
||||||
import org.apache.cassandra.transport.ProtocolVersion;
|
|
||||||
import org.apache.cassandra.utils.bytecomparable.ByteComparable.Version;
|
import org.apache.cassandra.utils.bytecomparable.ByteComparable.Version;
|
||||||
import org.apache.cassandra.utils.bytecomparable.ByteSource;
|
import org.apache.cassandra.utils.bytecomparable.ByteSource;
|
||||||
|
|
||||||
|
|
@ -52,7 +52,7 @@ public class ListType<T> extends CollectionType<List<T>>
|
||||||
private static final ConcurrentHashMap<AbstractType<?>, ListType> frozenInstances = new ConcurrentHashMap<>();
|
private static final ConcurrentHashMap<AbstractType<?>, ListType> frozenInstances = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private final AbstractType<T> elements;
|
private final AbstractType<T> elements;
|
||||||
public final ListSerializer<T> serializer;
|
private final ListSerializer<T> serializer;
|
||||||
private final boolean isMultiCell;
|
private final boolean isMultiCell;
|
||||||
|
|
||||||
public static ListType<?> getInstance(TypeParser parser) throws ConfigurationException, SyntaxException
|
public static ListType<?> getInstance(TypeParser parser) throws ConfigurationException, SyntaxException
|
||||||
|
|
@ -131,7 +131,7 @@ public class ListType<T> extends CollectionType<List<T>>
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AbstractType<?> freeze()
|
public ListType<T> freeze()
|
||||||
{
|
{
|
||||||
// freeze elements to match org.apache.cassandra.cql3.CQL3Type.Raw.RawCollection.freeze
|
// freeze elements to match org.apache.cassandra.cql3.CQL3Type.Raw.RawCollection.freeze
|
||||||
return isMultiCell ? getInstance(this.elements.freeze(), false) : this;
|
return isMultiCell ? getInstance(this.elements.freeze(), false) : this;
|
||||||
|
|
|
||||||
|
|
@ -44,10 +44,10 @@ import org.apache.cassandra.serializers.MapSerializer;
|
||||||
import org.apache.cassandra.serializers.MarshalException;
|
import org.apache.cassandra.serializers.MarshalException;
|
||||||
import org.apache.cassandra.transport.ProtocolVersion;
|
import org.apache.cassandra.transport.ProtocolVersion;
|
||||||
import org.apache.cassandra.utils.JsonUtils;
|
import org.apache.cassandra.utils.JsonUtils;
|
||||||
|
import org.apache.cassandra.utils.Pair;
|
||||||
import org.apache.cassandra.utils.bytecomparable.ByteComparable.Version;
|
import org.apache.cassandra.utils.bytecomparable.ByteComparable.Version;
|
||||||
import org.apache.cassandra.utils.bytecomparable.ByteSource;
|
import org.apache.cassandra.utils.bytecomparable.ByteSource;
|
||||||
import org.apache.cassandra.utils.bytecomparable.ByteSourceInverse;
|
import org.apache.cassandra.utils.bytecomparable.ByteSourceInverse;
|
||||||
import org.apache.cassandra.utils.Pair;
|
|
||||||
|
|
||||||
public class MapType<K, V> extends CollectionType<Map<K, V>>
|
public class MapType<K, V> extends CollectionType<Map<K, V>>
|
||||||
{
|
{
|
||||||
|
|
@ -153,7 +153,7 @@ public class MapType<K, V> extends CollectionType<Map<K, V>>
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AbstractType<?> freeze()
|
public MapType<K, V> freeze()
|
||||||
{
|
{
|
||||||
// freeze key/value to match org.apache.cassandra.cql3.CQL3Type.Raw.RawCollection.freeze
|
// freeze key/value to match org.apache.cassandra.cql3.CQL3Type.Raw.RawCollection.freeze
|
||||||
return isMultiCell ? getInstance(this.keys.freeze(), this.values.freeze(), false) : this;
|
return isMultiCell ? getInstance(this.keys.freeze(), this.values.freeze(), false) : this;
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,13 @@
|
||||||
package org.apache.cassandra.db.marshal;
|
package org.apache.cassandra.db.marshal;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.SortedSet;
|
||||||
|
import java.util.TreeSet;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
|
@ -117,7 +123,7 @@ public class SetType<T> extends CollectionType<Set<T>>
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AbstractType<?> freeze()
|
public SetType<T> freeze()
|
||||||
{
|
{
|
||||||
// freeze elements to match org.apache.cassandra.cql3.CQL3Type.Raw.RawCollection.freeze
|
// freeze elements to match org.apache.cassandra.cql3.CQL3Type.Raw.RawCollection.freeze
|
||||||
return isMultiCell ? getInstance(this.elements.freeze(), false) : this;
|
return isMultiCell ? getInstance(this.elements.freeze(), false) : this;
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import java.util.stream.Collectors;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|
@ -136,6 +137,12 @@ public class UserType extends TupleType implements SchemaElement
|
||||||
return type(i);
|
return type(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AbstractType<?> fieldType(CellPath path)
|
||||||
|
{
|
||||||
|
int field = ByteBufferUtil.getUnsignedShort(path.get(0), 0);
|
||||||
|
return fieldType(field);
|
||||||
|
}
|
||||||
|
|
||||||
public List<AbstractType<?>> fieldTypes()
|
public List<AbstractType<?>> fieldTypes()
|
||||||
{
|
{
|
||||||
return types;
|
return types;
|
||||||
|
|
@ -146,6 +153,11 @@ public class UserType extends TupleType implements SchemaElement
|
||||||
return fieldNames.get(i);
|
return fieldNames.get(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public FieldIdentifier fieldName(CellPath path)
|
||||||
|
{
|
||||||
|
return fieldNames.get(fieldPosition(path));
|
||||||
|
}
|
||||||
|
|
||||||
public String fieldNameAsString(int i)
|
public String fieldNameAsString(int i)
|
||||||
{
|
{
|
||||||
return stringFieldNames.get(i);
|
return stringFieldNames.get(i);
|
||||||
|
|
@ -166,6 +178,11 @@ public class UserType extends TupleType implements SchemaElement
|
||||||
return fieldNames.indexOf(fieldName);
|
return fieldNames.indexOf(fieldName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int fieldPosition(CellPath path)
|
||||||
|
{
|
||||||
|
return Preconditions.checkElementIndex(ByteBufferUtil.getUnsignedShort(path.get(0), 0), fieldNames.size());
|
||||||
|
}
|
||||||
|
|
||||||
public CellPath cellPathForField(FieldIdentifier fieldName)
|
public CellPath cellPathForField(FieldIdentifier fieldName)
|
||||||
{
|
{
|
||||||
// we use the field position instead of the field name to allow for field renaming in ALTER TYPE statements
|
// we use the field position instead of the field name to allow for field renaming in ALTER TYPE statements
|
||||||
|
|
@ -177,7 +194,7 @@ public class UserType extends TupleType implements SchemaElement
|
||||||
return ShortType.instance;
|
return ShortType.instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ByteBuffer serializeForNativeProtocol(Iterator<Cell<?>> cells, ProtocolVersion protocolVersion)
|
public ByteBuffer serializeForNativeProtocol(Iterator<Cell<?>> cells)
|
||||||
{
|
{
|
||||||
assert isMultiCell;
|
assert isMultiCell;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -386,10 +386,10 @@ public abstract class AbstractBTreePartition implements Partition, Iterable<Row>
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj)
|
public boolean equals(Object obj)
|
||||||
{
|
{
|
||||||
if (!(obj instanceof PartitionUpdate))
|
if (!(obj instanceof AbstractBTreePartition))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
PartitionUpdate that = (PartitionUpdate) obj;
|
AbstractBTreePartition that = (AbstractBTreePartition) obj;
|
||||||
BTreePartitionData a = this.holder(), b = that.holder();
|
BTreePartitionData a = this.holder(), b = that.holder();
|
||||||
return partitionKey.equals(that.partitionKey)
|
return partitionKey.equals(that.partitionKey)
|
||||||
&& metadata().id.equals(that.metadata().id)
|
&& metadata().id.equals(that.metadata().id)
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import org.apache.cassandra.db.DecoratedKey;
|
||||||
import org.apache.cassandra.db.DeletionInfo;
|
import org.apache.cassandra.db.DeletionInfo;
|
||||||
import org.apache.cassandra.db.RegularAndStaticColumns;
|
import org.apache.cassandra.db.RegularAndStaticColumns;
|
||||||
import org.apache.cassandra.db.rows.*;
|
import org.apache.cassandra.db.rows.*;
|
||||||
|
import org.apache.cassandra.utils.btree.BTree;
|
||||||
|
|
||||||
public class FilteredPartition extends ImmutableBTreePartition
|
public class FilteredPartition extends ImmutableBTreePartition
|
||||||
{
|
{
|
||||||
|
|
@ -43,6 +44,11 @@ public class FilteredPartition extends ImmutableBTreePartition
|
||||||
return new FilteredPartition(iterator);
|
return new FilteredPartition(iterator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Row getAtIdx(int idx)
|
||||||
|
{
|
||||||
|
return BTree.findByIndex(holder.tree, idx);
|
||||||
|
}
|
||||||
|
|
||||||
public RowIterator rowIterator()
|
public RowIterator rowIterator()
|
||||||
{
|
{
|
||||||
final Iterator<Row> iter = iterator();
|
final Iterator<Row> iter = iterator();
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ import org.apache.cassandra.db.filter.ColumnFilter;
|
||||||
public interface Partition
|
public interface Partition
|
||||||
{
|
{
|
||||||
public TableMetadata metadata();
|
public TableMetadata metadata();
|
||||||
|
|
||||||
public DecoratedKey partitionKey();
|
public DecoratedKey partitionKey();
|
||||||
public DeletionTime partitionLevelDeletion();
|
public DeletionTime partitionLevelDeletion();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -249,7 +249,8 @@ public class PartitionUpdate extends AbstractBTreePartition
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected boolean canHaveShadowedData()
|
@Override
|
||||||
|
public boolean canHaveShadowedData()
|
||||||
{
|
{
|
||||||
return canHaveShadowedData;
|
return canHaveShadowedData;
|
||||||
}
|
}
|
||||||
|
|
@ -586,6 +587,15 @@ public class PartitionUpdate extends AbstractBTreePartition
|
||||||
return new PartitionUpdate(metadata, metadata.epoch, key, holder, deletionInfo, canHaveShadowedData);
|
return new PartitionUpdate(metadata, metadata.epoch, key, holder, deletionInfo, canHaveShadowedData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj)
|
||||||
|
{
|
||||||
|
if (!(obj instanceof PartitionUpdate))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return super.equals(obj);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for building partition updates geared towards human.
|
* Interface for building partition updates geared towards human.
|
||||||
* <p>
|
* <p>
|
||||||
|
|
@ -914,6 +924,15 @@ public class PartitionUpdate extends AbstractBTreePartition
|
||||||
this(metadata, key, columns, initialRowCapacity, canHaveShadowedData, Rows.EMPTY_STATIC_ROW, MutableDeletionInfo.live(), BTree.empty());
|
this(metadata, key, columns, initialRowCapacity, canHaveShadowedData, Rows.EMPTY_STATIC_ROW, MutableDeletionInfo.live(), BTree.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder(TableMetadata metadata,
|
||||||
|
DecoratedKey key,
|
||||||
|
RegularAndStaticColumns columns,
|
||||||
|
Row staticRow,
|
||||||
|
int initialRowCapacity)
|
||||||
|
{
|
||||||
|
this(metadata, key, columns, initialRowCapacity, true, staticRow, MutableDeletionInfo.live(), BTree.empty());
|
||||||
|
}
|
||||||
|
|
||||||
private Builder(TableMetadata metadata,
|
private Builder(TableMetadata metadata,
|
||||||
DecoratedKey key,
|
DecoratedKey key,
|
||||||
RegularAndStaticColumns columns,
|
RegularAndStaticColumns columns,
|
||||||
|
|
@ -1090,6 +1109,14 @@ public class PartitionUpdate extends AbstractBTreePartition
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder updateAllTimestampAndLocalDeletionTime(long newTimestamp, int newLocalDeletionTime)
|
||||||
|
{
|
||||||
|
deletionInfo.updateAllTimestampAndLocalDeletionTime(newTimestamp - 1, newLocalDeletionTime);
|
||||||
|
tree = BTree.<Row, Row>transformAndFilter(tree, (x) -> x.updateAllTimestampAndLocalDeletionTime(newTimestamp, newLocalDeletionTime));
|
||||||
|
staticRow = this.staticRow.updateAllTimestampAndLocalDeletionTime(newTimestamp, newLocalDeletionTime);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -117,6 +117,13 @@ public abstract class AbstractCell<V> extends Cell<V>
|
||||||
return new BufferCell(column, isTombstone() ? newTimestamp - 1 : newTimestamp, ttl(), localDeletionTime(), buffer(), path());
|
return new BufferCell(column, isTombstone() ? newTimestamp - 1 : newTimestamp, ttl(), localDeletionTime(), buffer(), path());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ColumnData updateAllTimestampAndLocalDeletionTime(long newTimestamp, int newLocalDeletionTime)
|
||||||
|
{
|
||||||
|
long localDeletionTime = localDeletionTime() != NO_DELETION_TIME ? newLocalDeletionTime : NO_DELETION_TIME;
|
||||||
|
return new BufferCell(column, isTombstone() ? newTimestamp - 1 : newTimestamp, ttl(), localDeletionTime, buffer(), path());
|
||||||
|
}
|
||||||
|
|
||||||
public int dataSize()
|
public int dataSize()
|
||||||
{
|
{
|
||||||
CellPath path = path();
|
CellPath path = path();
|
||||||
|
|
|
||||||
|
|
@ -267,7 +267,7 @@ public class BTreeRow extends AbstractRow
|
||||||
|
|
||||||
public Cell<?> getCell(ColumnMetadata c)
|
public Cell<?> getCell(ColumnMetadata c)
|
||||||
{
|
{
|
||||||
assert !c.isComplex();
|
assert !c.isComplex(): String.format("Column %s.%s#%s", c.ksName, c.cfName, c.name);
|
||||||
return (Cell<?>) BTree.<Object>find(btree, ColumnMetadata.asymmetricColumnDataComparator, c);
|
return (Cell<?>) BTree.<Object>find(btree, ColumnMetadata.asymmetricColumnDataComparator, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -445,6 +445,18 @@ public class BTreeRow extends AbstractRow
|
||||||
return transformAndFilter(newInfo, newDeletion, (cd) -> cd.updateAllTimestamp(newTimestamp));
|
return transformAndFilter(newInfo, newDeletion, (cd) -> cd.updateAllTimestamp(newTimestamp));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Row updateAllTimestampAndLocalDeletionTime(long newTimestamp, int newLocalDeletionTime)
|
||||||
|
{
|
||||||
|
LivenessInfo newInfo = primaryKeyLivenessInfo.isEmpty() ? primaryKeyLivenessInfo : primaryKeyLivenessInfo.withUpdatedTimestampAndLocalDeletionTime(newTimestamp, newLocalDeletionTime);
|
||||||
|
// If the deletion is shadowable and the row has a timestamp, we'll forced the deletion timestamp to be less than the row one, so we
|
||||||
|
// should get rid of said deletion.
|
||||||
|
Deletion newDeletion = deletion.isLive() || (deletion.isShadowable() && !primaryKeyLivenessInfo.isEmpty())
|
||||||
|
? Deletion.LIVE
|
||||||
|
: new Deletion(DeletionTime.buildUnsafeWithUnsignedInteger(newTimestamp - 1, newLocalDeletionTime), deletion.isShadowable());
|
||||||
|
return transformAndFilter(newInfo, newDeletion, (cd) -> cd.updateAllTimestampAndLocalDeletionTime(newTimestamp, newLocalDeletionTime));
|
||||||
|
}
|
||||||
|
|
||||||
public Row withRowDeletion(DeletionTime newDeletion)
|
public Row withRowDeletion(DeletionTime newDeletion)
|
||||||
{
|
{
|
||||||
// Note that:
|
// Note that:
|
||||||
|
|
|
||||||
|
|
@ -284,6 +284,7 @@ public abstract class ColumnData implements IMeasurableMemory
|
||||||
* This exists for the Paxos path, see {@link PartitionUpdate#updateAllTimestamp} for additional details.
|
* This exists for the Paxos path, see {@link PartitionUpdate#updateAllTimestamp} for additional details.
|
||||||
*/
|
*/
|
||||||
public abstract ColumnData updateAllTimestamp(long newTimestamp);
|
public abstract ColumnData updateAllTimestamp(long newTimestamp);
|
||||||
|
public abstract ColumnData updateAllTimestampAndLocalDeletionTime(long newTimestamp, int newLocalDeletionTime);
|
||||||
|
|
||||||
public abstract ColumnData markCounterLocalToBeCleared();
|
public abstract ColumnData markCounterLocalToBeCleared();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -264,6 +264,13 @@ public class ComplexColumnData extends ColumnData implements Iterable<Cell<?>>
|
||||||
return transformAndFilter(newDeletion, (cell) -> (Cell<?>) cell.updateAllTimestamp(newTimestamp));
|
return transformAndFilter(newDeletion, (cell) -> (Cell<?>) cell.updateAllTimestamp(newTimestamp));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ColumnData updateAllTimestampAndLocalDeletionTime(long newTimestamp, int newLocalDeletionTime)
|
||||||
|
{
|
||||||
|
DeletionTime newDeletion = complexDeletion.isLive() ? complexDeletion : DeletionTime.buildUnsafeWithUnsignedInteger(newTimestamp - 1, newLocalDeletionTime);
|
||||||
|
return transformAndFilter(newDeletion, (cell) -> (Cell<?>) cell.updateAllTimestampAndLocalDeletionTime(newTimestamp, newLocalDeletionTime));
|
||||||
|
}
|
||||||
|
|
||||||
public long maxTimestamp()
|
public long maxTimestamp()
|
||||||
{
|
{
|
||||||
long timestamp = complexDeletion.markedForDeleteAt();
|
long timestamp = complexDeletion.markedForDeleteAt();
|
||||||
|
|
|
||||||
|
|
@ -299,6 +299,8 @@ public interface Row extends Unfiltered, Iterable<ColumnData>, IMeasurableMemory
|
||||||
*/
|
*/
|
||||||
public Row updateAllTimestamp(long newTimestamp);
|
public Row updateAllTimestamp(long newTimestamp);
|
||||||
|
|
||||||
|
public Row updateAllTimestampAndLocalDeletionTime(long newTimestamp, int newLocalDeletionTime);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a copy of this row with the new deletion as row deletion if it is more recent
|
* Returns a copy of this row with the new deletion as row deletion if it is more recent
|
||||||
* than the current row deletion.
|
* than the current row deletion.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.cassandra.db.virtual;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
|
||||||
|
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||||
|
import org.apache.cassandra.cql3.statements.schema.CreateTableStatement;
|
||||||
|
import org.apache.cassandra.schema.TableMetadata;
|
||||||
|
import org.apache.cassandra.service.accord.AccordService;
|
||||||
|
import org.apache.cassandra.service.accord.IAccordService;
|
||||||
|
|
||||||
|
public class AccordVirtualTables
|
||||||
|
{
|
||||||
|
private AccordVirtualTables()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Collection<VirtualTable> getAll(String keyspace)
|
||||||
|
{
|
||||||
|
if (!DatabaseDescriptor.getAccordTransactionsEnabled())
|
||||||
|
return Collections.emptyList();
|
||||||
|
|
||||||
|
return Arrays.asList(
|
||||||
|
new Epoch(keyspace)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public static final class Epoch extends AbstractVirtualTable
|
||||||
|
{
|
||||||
|
|
||||||
|
protected Epoch(String keyspace)
|
||||||
|
{
|
||||||
|
super(parse(keyspace, "Accord Epochs",
|
||||||
|
"CREATE TABLE accord_epochs(\n" +
|
||||||
|
" epoch bigint,\n" +
|
||||||
|
" PRIMARY KEY ( (epoch) )" +
|
||||||
|
")"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DataSet data()
|
||||||
|
{
|
||||||
|
IAccordService accord = AccordService.instance();
|
||||||
|
accord.createEpochFromConfigUnsafe();
|
||||||
|
|
||||||
|
long epoch = accord.currentEpoch();
|
||||||
|
|
||||||
|
SimpleDataSet result = new SimpleDataSet(metadata());
|
||||||
|
result.row(epoch);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TableMetadata parse(String keyspace, String comment, String query)
|
||||||
|
{
|
||||||
|
return CreateTableStatement.parse(query, keyspace)
|
||||||
|
.comment(comment)
|
||||||
|
.kind(TableMetadata.Kind.VIRTUAL)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -69,6 +69,7 @@ public final class SystemViewsKeyspace extends VirtualKeyspace
|
||||||
.addAll(LocalRepairTables.getAll(VIRTUAL_VIEWS))
|
.addAll(LocalRepairTables.getAll(VIRTUAL_VIEWS))
|
||||||
.addAll(CIDRFilteringMetricsTable.getAll(VIRTUAL_VIEWS))
|
.addAll(CIDRFilteringMetricsTable.getAll(VIRTUAL_VIEWS))
|
||||||
.addAll(StorageAttachedIndexTables.getAll(VIRTUAL_VIEWS))
|
.addAll(StorageAttachedIndexTables.getAll(VIRTUAL_VIEWS))
|
||||||
|
.addAll(AccordVirtualTables.getAll(VIRTUAL_VIEWS))
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.cassandra.dht;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
import accord.api.RoutingKey;
|
||||||
|
import accord.primitives.Ranges;
|
||||||
|
import accord.utils.Invariants;
|
||||||
|
import org.apache.cassandra.service.accord.api.AccordRoutingKey;
|
||||||
|
|
||||||
|
import static accord.utils.Invariants.checkArgument;
|
||||||
|
import static java.math.BigInteger.ONE;
|
||||||
|
import static java.math.BigInteger.ZERO;
|
||||||
|
|
||||||
|
public class AccordBytesSplitter extends AccordSplitter
|
||||||
|
{
|
||||||
|
final int byteLength;
|
||||||
|
|
||||||
|
protected AccordBytesSplitter(Ranges ranges)
|
||||||
|
{
|
||||||
|
int bytesLength = 0;
|
||||||
|
for (accord.primitives.Range range : ranges)
|
||||||
|
{
|
||||||
|
bytesLength = Integer.max(bytesLength, byteLength(range.start()));
|
||||||
|
bytesLength = Integer.max(bytesLength, byteLength(range.end()));
|
||||||
|
}
|
||||||
|
this.byteLength = bytesLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
BigInteger minimumValue()
|
||||||
|
{
|
||||||
|
return ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
BigInteger maximumValue()
|
||||||
|
{
|
||||||
|
return ONE.shiftLeft(8 * byteLength).subtract(ONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
BigInteger valueForToken(Token token)
|
||||||
|
{
|
||||||
|
byte[] bytes = ((ByteOrderedPartitioner.BytesToken) token).token;
|
||||||
|
checkArgument(bytes.length <= byteLength);
|
||||||
|
BigInteger value = ZERO;
|
||||||
|
for (int i = 0 ; i < bytes.length ; ++i)
|
||||||
|
value = value.add(BigInteger.valueOf(bytes[i] & 0xffL).shiftLeft((byteLength - 1 - i) * 8));
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Token tokenForValue(BigInteger value)
|
||||||
|
{
|
||||||
|
Invariants.checkArgument(value.compareTo(ZERO) >= 0);
|
||||||
|
byte[] bytes = new byte[byteLength];
|
||||||
|
for (int i = 0 ; i < bytes.length ; ++i)
|
||||||
|
bytes[i] = value.shiftRight((byteLength - 1 - i) * 8).byteValue();
|
||||||
|
return new ByteOrderedPartitioner.BytesToken(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int byteLength(RoutingKey routingKey)
|
||||||
|
{
|
||||||
|
return byteLength(((AccordRoutingKey) routingKey).token());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int byteLength(Token token)
|
||||||
|
{
|
||||||
|
return ((ByteOrderedPartitioner.BytesToken) token).token.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,103 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.cassandra.dht;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
import accord.local.ShardDistributor;
|
||||||
|
import org.apache.cassandra.service.accord.TokenRange;
|
||||||
|
import org.apache.cassandra.service.accord.api.AccordRoutingKey;
|
||||||
|
import org.apache.cassandra.service.accord.api.AccordRoutingKey.SentinelKey;
|
||||||
|
import org.apache.cassandra.service.accord.api.AccordRoutingKey.TokenKey;
|
||||||
|
|
||||||
|
import static java.math.BigInteger.ZERO;
|
||||||
|
|
||||||
|
public abstract class AccordSplitter implements ShardDistributor.EvenSplit.Splitter<BigInteger>
|
||||||
|
{
|
||||||
|
abstract BigInteger valueForToken(Token token);
|
||||||
|
abstract Token tokenForValue(BigInteger value);
|
||||||
|
abstract BigInteger minimumValue();
|
||||||
|
abstract BigInteger maximumValue();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BigInteger sizeOf(accord.primitives.Range range)
|
||||||
|
{
|
||||||
|
// note: minimum value
|
||||||
|
BigInteger start = range.start() instanceof SentinelKey ? minimumValue() : valueForToken(((AccordRoutingKey)range.start()).token());
|
||||||
|
BigInteger end = range.end() instanceof SentinelKey ? maximumValue() : valueForToken(((AccordRoutingKey)range.end()).token());
|
||||||
|
return end.subtract(start);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public accord.primitives.Range subRange(accord.primitives.Range range, BigInteger startOffset, BigInteger endOffset)
|
||||||
|
{
|
||||||
|
AccordRoutingKey startBound = (AccordRoutingKey)range.start();
|
||||||
|
AccordRoutingKey endBound = (AccordRoutingKey)range.end();
|
||||||
|
|
||||||
|
BigInteger start = startBound instanceof SentinelKey ? minimumValue() : valueForToken(startBound.token());
|
||||||
|
BigInteger end = endBound instanceof SentinelKey ? maximumValue() : valueForToken(endBound.token());
|
||||||
|
BigInteger sizeOfRange = end.subtract(start);
|
||||||
|
|
||||||
|
String keyspace = startBound.keyspace();
|
||||||
|
return new TokenRange(startOffset.equals(ZERO) ? startBound : new TokenKey(keyspace, tokenForValue(start.add(startOffset))),
|
||||||
|
endOffset.equals(sizeOfRange) ? endBound : new TokenKey(keyspace, tokenForValue(start.add(endOffset))));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BigInteger zero()
|
||||||
|
{
|
||||||
|
return ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BigInteger add(BigInteger a, BigInteger b)
|
||||||
|
{
|
||||||
|
return a.add(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BigInteger subtract(BigInteger a, BigInteger b)
|
||||||
|
{
|
||||||
|
return a.subtract(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BigInteger divide(BigInteger a, int i)
|
||||||
|
{
|
||||||
|
return a.divide(BigInteger.valueOf(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BigInteger multiply(BigInteger a, int i)
|
||||||
|
{
|
||||||
|
return a.multiply(BigInteger.valueOf(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int min(BigInteger v, int i)
|
||||||
|
{
|
||||||
|
return v.min(BigInteger.valueOf(i)).intValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(BigInteger a, BigInteger b)
|
||||||
|
{
|
||||||
|
return a.compareTo(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.cassandra.dht;
|
package org.apache.cassandra.dht;
|
||||||
|
|
||||||
|
import accord.primitives.Ranges;
|
||||||
import org.apache.cassandra.schema.TableMetadata;
|
import org.apache.cassandra.schema.TableMetadata;
|
||||||
import org.apache.cassandra.schema.Schema;
|
import org.apache.cassandra.schema.Schema;
|
||||||
import org.apache.cassandra.db.BufferDecoratedKey;
|
import org.apache.cassandra.db.BufferDecoratedKey;
|
||||||
|
|
@ -44,6 +45,7 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.concurrent.ThreadLocalRandom;
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
|
|
@ -128,6 +130,12 @@ public class ByteOrderedPartitioner implements IPartitioner
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int tokenHash()
|
||||||
|
{
|
||||||
|
return hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double size(Token next)
|
public double size(Token next)
|
||||||
{
|
{
|
||||||
|
|
@ -138,8 +146,51 @@ public class ByteOrderedPartitioner implements IPartitioner
|
||||||
@Override
|
@Override
|
||||||
public Token nextValidToken()
|
public Token nextValidToken()
|
||||||
{
|
{
|
||||||
throw new UnsupportedOperationException(String.format("Token type %s does not support token allocation.",
|
// find first byte we can increment
|
||||||
getClass().getSimpleName()));
|
int i = token.length - 1;
|
||||||
|
while (i >= 0)
|
||||||
|
{
|
||||||
|
if (token[i] != -1)
|
||||||
|
break;
|
||||||
|
--i;
|
||||||
|
}
|
||||||
|
if (i == -1)
|
||||||
|
return new BytesToken(Arrays.copyOf(token, token.length + 1));
|
||||||
|
|
||||||
|
// increment and fill remainder with zeros
|
||||||
|
byte[] newToken = token.clone();
|
||||||
|
++newToken[i];
|
||||||
|
Arrays.fill(newToken, i + 1, newToken.length, (byte)0);
|
||||||
|
return new BytesToken(newToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Token decreaseSlightly()
|
||||||
|
{
|
||||||
|
if (token.length == 0)
|
||||||
|
throw new IndexOutOfBoundsException("Cannot create a smaller token the MINIMUM");
|
||||||
|
|
||||||
|
// find first byte we can decrement
|
||||||
|
int i = token.length - 1;
|
||||||
|
while (i >= 0)
|
||||||
|
{
|
||||||
|
if (token[i] != 0)
|
||||||
|
break;
|
||||||
|
--i;
|
||||||
|
}
|
||||||
|
if (i == -1)
|
||||||
|
{
|
||||||
|
byte[] newToken = Arrays.copyOf(token, token.length - 1);
|
||||||
|
if (newToken.length > 0)
|
||||||
|
newToken[newToken.length - 1] = (byte)-1;
|
||||||
|
return new BytesToken(newToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
// decrement and fill remainder with -1
|
||||||
|
byte[] newToken = token.clone();
|
||||||
|
--newToken[i];
|
||||||
|
Arrays.fill(newToken, i + 1, newToken.length, (byte)-1);
|
||||||
|
return new BytesToken(newToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -339,4 +390,10 @@ public class ByteOrderedPartitioner implements IPartitioner
|
||||||
{
|
{
|
||||||
return BytesType.instance;
|
return BytesType.instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Function<Ranges, AccordSplitter> accordSplitter()
|
||||||
|
{
|
||||||
|
return AccordBytesSplitter::new;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -80,4 +80,11 @@ abstract class ComparableObjectToken<C extends Comparable<C>> extends Token
|
||||||
throw new UnsupportedOperationException(String.format("Token type %s does not support token allocation.",
|
throw new UnsupportedOperationException(String.format("Token type %s does not support token allocation.",
|
||||||
getClass().getSimpleName()));
|
getClass().getSimpleName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Token decreaseSlightly()
|
||||||
|
{
|
||||||
|
throw new UnsupportedOperationException(String.format("Token type %s does not support token allocation.",
|
||||||
|
getClass().getSimpleName()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||||
import org.apache.cassandra.db.DecoratedKey;
|
import org.apache.cassandra.db.DecoratedKey;
|
||||||
|
|
@ -144,6 +145,13 @@ public interface IPartitioner
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Function<accord.primitives.Ranges, AccordSplitter> accordSplitter();
|
||||||
|
|
||||||
|
default boolean isFixedLength()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
default public int getMaxTokenSize()
|
default public int getMaxTokenSize()
|
||||||
{
|
{
|
||||||
return Integer.MIN_VALUE;
|
return Integer.MIN_VALUE;
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,9 @@ import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import accord.primitives.Ranges;
|
||||||
import org.apache.cassandra.db.DecoratedKey;
|
import org.apache.cassandra.db.DecoratedKey;
|
||||||
import org.apache.cassandra.db.CachedHashDecoratedKey;
|
import org.apache.cassandra.db.CachedHashDecoratedKey;
|
||||||
import org.apache.cassandra.db.marshal.AbstractType;
|
import org.apache.cassandra.db.marshal.AbstractType;
|
||||||
|
|
@ -174,6 +176,12 @@ public class LocalPartitioner implements IPartitioner
|
||||||
return prime + token.hashCode();
|
return prime + token.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int tokenHash()
|
||||||
|
{
|
||||||
|
return hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj)
|
public boolean equals(Object obj)
|
||||||
{
|
{
|
||||||
|
|
@ -203,4 +211,10 @@ public class LocalPartitioner implements IPartitioner
|
||||||
return EMPTY_SIZE + ObjectSizes.sizeOnHeapOf(token);
|
return EMPTY_SIZE + ObjectSizes.sizeOnHeapOf(token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Function<Ranges, AccordSplitter> accordSplitter()
|
||||||
|
{
|
||||||
|
return AccordBytesSplitter::new;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue