From 7a8c3a6524471eb37e66550f1281beade6f699c1 Mon Sep 17 00:00:00 2001 From: Pengyu Chen <48903014+trin1t@users.noreply.github.com> Date: Fri, 13 May 2022 21:27:56 +0800 Subject: [PATCH 001/436] [IOTDB-3169] Add Library-UDF to IOTDB Distribution (#5874) --- distribution/pom.xml | 8 ++ distribution/src/assembly/library-udf.xml | 49 +++++++++ .../src/assembly/tools/register-UDF.bat | 102 +++++++++++++++++ .../src/assembly/tools/register-UDF.sh | 104 ++++++++++++++++++ 4 files changed, 263 insertions(+) create mode 100644 distribution/src/assembly/library-udf.xml create mode 100644 library-udf/src/assembly/tools/register-UDF.bat create mode 100644 library-udf/src/assembly/tools/register-UDF.sh diff --git a/distribution/pom.xml b/distribution/pom.xml index b9b4dad132be..327bb2e8b005 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -75,6 +75,7 @@ src/assembly/grafana-connector.xml src/assembly/client-cpp.xml src/assembly/grafana-plugin.xml + src/assembly/library-udf.xml apache-iotdb-${project.version} @@ -111,6 +112,7 @@ apache-iotdb-${project.version}-grafana-connector-bin.zip apache-iotdb-${project.version}-client-cpp-${os.classifier}-bin.zip apache-iotdb-${project.version}-grafana-plugin-bin.zip + apache-iotdb-${project.version}-library-udf-bin.zip @@ -164,5 +166,11 @@ ${project.version} pom + + org.apache.iotdb + library-udf + ${project.version} + jar + diff --git a/distribution/src/assembly/library-udf.xml b/distribution/src/assembly/library-udf.xml new file mode 100644 index 000000000000..1501cde10410 --- /dev/null +++ b/distribution/src/assembly/library-udf.xml @@ -0,0 +1,49 @@ + + + + library-udf-bin + + dir + zip + + apache-iotdb-${project.version}-library-udf-bin + + + + org.apache.iotdb:library-udf:jar:${project.version} + + ext/udf + ${artifact.artifactId}.${artifact.extension} + false + + + + + ${maven.multiModuleProjectDirectory}/library-udf/src/assembly/tools + tools + 0755 + + + + common-files.xml + + diff --git a/library-udf/src/assembly/tools/register-UDF.bat b/library-udf/src/assembly/tools/register-UDF.bat new file mode 100644 index 000000000000..f2a0b22b3551 --- /dev/null +++ b/library-udf/src/assembly/tools/register-UDF.bat @@ -0,0 +1,102 @@ +@REM +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM + +@REM Parameters +@set host=127.0.0.1 +@set rpcPort=6667 +@set user=root +@set pass=root + + +@REM Data Profiling +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function distinct as 'org.apache.iotdb.quality.dprofile.UDTFDistinct'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function histogram as 'org.apache.iotdb.quality.dprofile.UDTFHistogram'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function integral as 'org.apache.iotdb.quality.dprofile.UDAFIntegral'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function integralavg as 'org.apache.iotdb.quality.dprofile.UDAFIntegralAvg'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function mad as 'org.apache.iotdb.quality.dprofile.UDAFMad'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function median as 'org.apache.iotdb.quality.dprofile.UDAFMedian'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function mode as 'org.apache.iotdb.quality.dprofile.UDAFMode'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function percentile as 'org.apache.iotdb.quality.dprofile.UDAFPercentile'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function period as 'org.apache.iotdb.quality.dprofile.UDAFPeriod'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function qlb as 'org.apache.iotdb.quality.dprofile.UDTFQLB'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function re_sample as 'org.apache.iotdb.quality.dprofile.UDTFResample'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function sample as 'org.apache.iotdb.quality.dprofile.UDTFSample'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function segment as 'org.apache.iotdb.quality.dprofile.UDTFSegment'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function skew as 'org.apache.iotdb.quality.dprofile.UDAFSkew'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function spread as 'org.apache.iotdb.quality.dprofile.UDAFSpread'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function stddev as 'org.apache.iotdb.quality.dprofile.UDAFStddev'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function minmax as 'org.apache.iotdb.quality.dprofile.UDTFMinMax'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function zscore as 'org.apache.iotdb.quality.dprofile.UDTFZScore'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function spline as 'org.apache.iotdb.quality.dprofile.UDTFSpline'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function mvavg as 'org.apache.iotdb.quality.dprofile.UDTFMvAvg'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function acf as 'org.apache.iotdb.quality.dprofile.UDTFACF'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function pacf as 'org.apache.iotdb.quality.dprofile.UDTFPACF'" + + +@REM Data Quality +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function completeness as 'org.apache.iotdb.quality.dquality.UDTFCompleteness'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function consistency as 'org.apache.iotdb.quality.dquality.UDTFConsistency'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function timeliness as 'org.apache.iotdb.quality.dquality.UDTFTimeliness'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function validity as 'org.apache.iotdb.quality.dquality.UDTFValidity'" + + +@REM Data Repairing +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function timestamprepair as 'org.apache.iotdb.quality.drepair.UDTFTimestampRepair'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function valuerepair as 'org.apache.iotdb.quality.drepair.UDTFValueRepair'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function valuefill as 'org.apache.iotdb.quality.drepair.UDTFValueFill'" + + +@REM Data Matching +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function cov as 'org.apache.iotdb.quality.dmatch.UDAFCov'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function xcorr as 'org.apache.iotdb.quality.dmatch.UDTFXCorr'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function dtw as 'org.apache.iotdb.quality.dmatch.UDAFDtw'" +call ../bin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function ptnsym as 'org.apache.iotdb.quality.dmatch.UDTFPtnSym'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function pearson as 'org.apache.iotdb.quality.dmatch.UDAFPearson'" + + +@REM Anomaly Detection +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function ksigma as 'org.apache.iotdb.quality.anomaly.UDTFKSigma'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function lof as 'org.apache.iotdb.quality.anomaly.UDTFLOF'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function range as 'org.apache.iotdb.quality.anomaly.UDTFRange'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function iqr as 'org.apache.iotdb.quality.anomaly.UDTFIQR'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function twosidedfilter as 'org.apache.iotdb.quality.anomaly.UDTFTwoSidedFilter'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function missdetect as 'org.apache.iotdb.quality.anomaly.UDTFMissDetect'" + + +@REM Frequency Domain +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function fft as 'org.apache.iotdb.quality.frequency.UDTFFFT'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function stft as 'org.apache.iotdb.quality.frequency.UDTFSTFT'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function conv as 'org.apache.iotdb.quality.frequency.UDTFConv'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function deconv as 'org.apache.iotdb.quality.frequency.UDTFDeconv'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function lowpass as 'org.apache.iotdb.quality.frequency.UDTFLowPass'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function highpass as 'org.apache.iotdb.quality.frequency.UDTFHighPass'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function dwt as 'org.apache.iotdb.quality.frequency.UDTFDWT'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function idwt as 'org.apache.iotdb.quality.frequency.UDTFIDWT'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function ifft as 'org.apache.iotdb.quality.frequency.UDTFIFFT'" + + +@REM Series Discovery +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function consecutivesequences as 'org.apache.iotdb.quality.series.UDTFConsecutiveSequences'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function consecutivewindows as 'org.apache.iotdb.quality.series.UDTFConsecutiveWindows'" + +@REM String Processing +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function regexsplit as 'org.apache.iotdb.quality.string.UDTFRegexSplit'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function regexmatch as 'org.apache.iotdb.quality.string.UDTFRegexMatch'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function strreplace as 'org.apache.iotdb.quality.string.UDTFStrReplace'" +call ../sbin/start-cli.bat -h %host% -p %rpcPort% -u %user% -pw %pass% -e "create function regexreplace as 'org.apache.iotdb.quality.string.UDTFRegexReplace'" diff --git a/library-udf/src/assembly/tools/register-UDF.sh b/library-udf/src/assembly/tools/register-UDF.sh new file mode 100644 index 000000000000..aa1e94dc1726 --- /dev/null +++ b/library-udf/src/assembly/tools/register-UDF.sh @@ -0,0 +1,104 @@ +#!/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. +# + + +# Parameters +host=127.0.0.1 +rpcPort=6667 +user=root +pass=root + +# Data Profiling + +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function distinct as 'org.apache.iotdb.quality.dprofile.UDTFDistinct'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function histogram as 'org.apache.iotdb.quality.dprofile.UDTFHistogram'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function integral as 'org.apache.iotdb.quality.dprofile.UDAFIntegral'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function integralavg as 'org.apache.iotdb.quality.dprofile.UDAFIntegralAvg'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function mad as 'org.apache.iotdb.quality.dprofile.UDAFMad'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function median as 'org.apache.iotdb.quality.dprofile.UDAFMedian'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function mode as 'org.apache.iotdb.quality.dprofile.UDAFMode'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function percentile as 'org.apache.iotdb.quality.dprofile.UDAFPercentile'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function period as 'org.apache.iotdb.quality.dprofile.UDAFPeriod'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function qlb as 'org.apache.iotdb.quality.dprofile.UDTFQLB'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function re_sample as 'org.apache.iotdb.quality.dprofile.UDTFResample'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function sample as 'org.apache.iotdb.quality.dprofile.UDTFSample'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function segment as 'org.apache.iotdb.quality.dprofile.UDTFSegment'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function skew as 'org.apache.iotdb.quality.dprofile.UDAFSkew'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function spread as 'org.apache.iotdb.quality.dprofile.UDAFSpread'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function stddev as 'org.apache.iotdb.quality.dprofile.UDAFStddev'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function minmax as 'org.apache.iotdb.quality.dprofile.UDTFMinMax'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function zscore as 'org.apache.iotdb.quality.dprofile.UDTFZScore'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function spline as 'org.apache.iotdb.quality.dprofile.UDTFSpline'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function mvavg as 'org.apache.iotdb.quality.dprofile.UDTFMvAvg'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function acf as 'org.apache.iotdb.quality.dprofile.UDTFACF'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function pacf as 'org.apache.iotdb.quality.dprofile.UDTFPACF'" + + +# Data Quality +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function completeness as 'org.apache.iotdb.quality.dquality.UDTFCompleteness'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function consistency as 'org.apache.iotdb.quality.dquality.UDTFConsistency'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function timeliness as 'org.apache.iotdb.quality.dquality.UDTFTimeliness'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function validity as 'org.apache.iotdb.quality.dquality.UDTFValidity'" + + +# Data Repairing +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function timestamprepair as 'org.apache.iotdb.quality.drepair.UDTFTimestampRepair'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function valuerepair as 'org.apache.iotdb.quality.drepair.UDTFValueRepair'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function valuefill as 'org.apache.iotdb.quality.drepair.UDTFValueFill'" + + +# Data Matching +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function cov as 'org.apache.iotdb.quality.dmatch.UDAFCov'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function xcorr as 'org.apache.iotdb.quality.dmatch.UDTFXCorr'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function dtw as 'org.apache.iotdb.quality.dmatch.UDAFDtw'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function ptnsym as 'org.apache.iotdb.quality.dmatch.UDTFPtnSym'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function pearson as 'org.apache.iotdb.quality.dmatch.UDAFPearson'" + + +# Anomaly Detection +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function ksigma as 'org.apache.iotdb.quality.anomaly.UDTFKSigma'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function LOF as 'org.apache.iotdb.quality.anomaly.UDTFLOF'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function range as 'org.apache.iotdb.quality.anomaly.UDTFRange'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function twosidedfilter as 'org.apache.iotdb.quality.anomaly.UDTFTwoSidedFilter'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function iqr as 'org.apache.iotdb.quality.anomaly.UDTFIQR'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function missdetect as 'org.apache.iotdb.quality.anomaly.UDTFMissDetect'" + + +# Frequency Domain +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function fft as 'org.apache.iotdb.quality.frequency.UDTFFFT'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function stft as 'org.apache.iotdb.quality.frequency.UDTFSTFT'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function conv as 'org.apache.iotdb.quality.frequency.UDTFConv'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function deconv as 'org.apache.iotdb.quality.frequency.UDTFDeconv'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function lowpass as 'org.apache.iotdb.quality.frequency.UDTFLowPass'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function highpass as 'org.apache.iotdb.quality.frequency.UDTFHighPass'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function dwt as 'org.apache.iotdb.quality.frequency.UDTFDWT'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function idwt as 'org.apache.iotdb.quality.frequency.UDTFIDWT'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function ifft as 'org.apache.iotdb.quality.frequency.UDTFIFFT'" + +# Series Discovery +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function consecutivesequences as 'org.apache.iotdb.quality.series.UDTFConsecutiveSequences'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function consecutivewindows as 'org.apache.iotdb.quality.series.UDTFConsecutiveWindows'" + +# String Processing +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function regexsplit as 'org.apache.iotdb.quality.string.UDTFRegexSplit'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function regexmatch as 'org.apache.iotdb.quality.string.UDTFRegexMatch'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function strreplace as 'org.apache.iotdb.quality.string.UDTFStrReplace'" +../sbin/start-cli.sh -h $host -p $rpcPort -u $user -pw $pass -e "create function regexreplace as 'org.apache.iotdb.quality.string.UDTFRegexReplace'" + From a0e2aed850b9485f037a0ad6274fd4c8cc1033cc Mon Sep 17 00:00:00 2001 From: YongzaoDan <33111881+CRZbulabula@users.noreply.github.com> Date: Fri, 13 May 2022 23:47:55 +0800 Subject: [PATCH 002/436] [IOTDB-3179] Printing logs when get/getOrCreate Partition in ConfigNode --- .../consensus/response/DataPartitionResp.java | 4 ++ .../response/SchemaPartitionResp.java | 4 ++ .../confignode/manager/ConfigManager.java | 46 +++++++++++++++++-- 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/DataPartitionResp.java b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/DataPartitionResp.java index 7b1e01407b5a..53f3ed080045 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/DataPartitionResp.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/DataPartitionResp.java @@ -47,6 +47,10 @@ public void setDataPartition(DataPartition dataPartition) { this.dataPartition = dataPartition; } + public DataPartition getDataPartition() { + return dataPartition; + } + /** * Convert DataPartitionDataSet to TDataPartitionResp * diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/SchemaPartitionResp.java b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/SchemaPartitionResp.java index 2b99ea664f49..e73865e7d5ee 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/SchemaPartitionResp.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/SchemaPartitionResp.java @@ -47,6 +47,10 @@ public void setSchemaPartition(SchemaPartition schemaPartition) { this.schemaPartition = schemaPartition; } + public SchemaPartition getSchemaPartition() { + return schemaPartition; + } + public void convertToRpcSchemaPartitionResp(TSchemaPartitionResp resp) { resp.setStatus(status); diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java index 130eff010a09..47ebb6703468 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java @@ -286,7 +286,16 @@ public DataSet getSchemaPartition(PathPatternTree patternTree) { } getSchemaPartitionReq.setPartitionSlotsMap(partitionSlotsMap); - return partitionManager.getSchemaPartition(getSchemaPartitionReq); + SchemaPartitionResp resp = + (SchemaPartitionResp) partitionManager.getSchemaPartition(getSchemaPartitionReq); + + // TODO: Delete or hide this LOGGER before officially release. + LOGGER.info( + "GetSchemaPartition interface receive devicePaths: {}, return SchemaPartition: {}", + devicePaths, + resp.getSchemaPartition().getSchemaPartitionMap()); + + return resp; } else { SchemaPartitionResp dataSet = new SchemaPartitionResp(); dataSet.setStatus(status); @@ -320,7 +329,17 @@ public DataSet getOrCreateSchemaPartition(PathPatternTree patternTree) { } getOrCreateSchemaPartitionReq.setPartitionSlotsMap(partitionSlotsMap); - return partitionManager.getOrCreateSchemaPartition(getOrCreateSchemaPartitionReq); + SchemaPartitionResp resp = + (SchemaPartitionResp) + partitionManager.getOrCreateSchemaPartition(getOrCreateSchemaPartitionReq); + + // TODO: Delete or hide this LOGGER before officially release. + LOGGER.info( + "GetOrCreateSchemaPartition interface receive devicePaths: {}, return SchemaPartition: {}", + devicePaths, + resp.getSchemaPartition().getSchemaPartitionMap()); + + return resp; } else { SchemaPartitionResp dataSet = new SchemaPartitionResp(); dataSet.setStatus(status); @@ -332,7 +351,16 @@ public DataSet getOrCreateSchemaPartition(PathPatternTree patternTree) { public DataSet getDataPartition(GetDataPartitionReq getDataPartitionReq) { TSStatus status = confirmLeader(); if (status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) { - return partitionManager.getDataPartition(getDataPartitionReq); + DataPartitionResp resp = + (DataPartitionResp) partitionManager.getDataPartition(getDataPartitionReq); + + // TODO: Delete or hide this LOGGER before officially release. + LOGGER.info( + "GetDataPartition interface receive PartitionSlotsMap: {}, return DataPartition: {}", + getDataPartitionReq.getPartitionSlotsMap(), + resp.getDataPartition().getDataPartitionMap()); + + return resp; } else { DataPartitionResp dataSet = new DataPartitionResp(); dataSet.setStatus(status); @@ -344,7 +372,17 @@ public DataSet getDataPartition(GetDataPartitionReq getDataPartitionReq) { public DataSet getOrCreateDataPartition(GetOrCreateDataPartitionReq getOrCreateDataPartitionReq) { TSStatus status = confirmLeader(); if (status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) { - return partitionManager.getOrCreateDataPartition(getOrCreateDataPartitionReq); + DataPartitionResp resp = + (DataPartitionResp) + partitionManager.getOrCreateDataPartition(getOrCreateDataPartitionReq); + + // TODO: Delete or hide this LOGGER before officially release. + LOGGER.info( + "GetOrCreateDataPartition receive PartitionSlotsMap: {}, return DataPartition: {}", + getOrCreateDataPartitionReq.getPartitionSlotsMap(), + resp.getDataPartition().getDataPartitionMap()); + + return resp; } else { DataPartitionResp dataSet = new DataPartitionResp(); dataSet.setStatus(status); From 6a34de5006bf459bae0a34f5f473791ab3608f40 Mon Sep 17 00:00:00 2001 From: YongzaoDan <33111881+CRZbulabula@users.noreply.github.com> Date: Fri, 13 May 2022 23:48:03 +0800 Subject: [PATCH 003/436] [IOTDB-3054] Cluster heartbeat framework (#5873) --- .../conf/iotdb-confignode.properties | 18 ++++++ .../client/AsyncDataNodeClientPool.java | 17 ++++++ .../client/SyncConfigNodeClientPool.java | 2 +- .../client/handlers/HeartbeatHandler.java | 49 +++++++++++++++++ .../iotdb/confignode/conf/ConfigNodeConf.java | 24 +++++++- .../confignode/conf/ConfigNodeDescriptor.java | 10 ++++ .../conf/ConfigNodeStartupCheck.java | 2 +- .../request/write/ApplyConfigNodeReq.java | 2 +- .../response/DataNodeConfigurationResp.java | 2 +- .../confignode/manager/ConfigManager.java | 7 +++ .../confignode/manager/ConsensusManager.java | 4 +- .../iotdb/confignode/manager/Manager.java | 1 + .../confignode/manager/PartitionManager.java | 1 + .../manager/{ => load}/LoadManager.java | 55 +++++++++++++++++-- .../allocator/CopySetRegionAllocator.java | 2 +- .../allocator/IRegionAllocator.java | 2 +- .../{ => load}/balancer/RegionBalancer.java | 2 +- .../balancer/SeriesPartitionSlotBalancer.java | 2 +- .../load/heartbeat/HeartbeatCache.java | 51 +++++++++++++++++ .../load/heartbeat/HeartbeatPackage.java | 38 +++++++++++++ .../load/heartbeat/HeartbeatWindow.java | 51 +++++++++++++++++ .../load/heartbeat/IHeartbeatStatistic.java | 36 ++++++++++++ .../confignode/persistence/NodeInfo.java | 2 +- .../thrift/ConfigNodeRPCServiceProcessor.java | 2 +- .../request/ConfigRequestSerDeTest.java | 2 +- .../iotdb/commons/utils/NodeUrlUtils.java | 2 +- .../utils/ThriftConfigNodeSerDeUtils.java | 2 +- .../iotdb/commons/utils/NodeUrlUtilsTest.java | 2 +- .../utils/ThriftConfigNodeSerDeUtilsTest.java | 2 +- .../iotdb/db/client/ConfigNodeClient.java | 2 +- .../org/apache/iotdb/db/service/DataNode.java | 2 +- .../thrift/impl/InternalServiceImpl.java | 8 +++ thrift-commons/src/main/thrift/common.thrift | 13 +++++ .../src/main/thrift/confignode.thrift | 13 ++--- thrift/src/main/thrift/mpp.thrift | 6 ++ 35 files changed, 403 insertions(+), 33 deletions(-) create mode 100644 confignode/src/main/java/org/apache/iotdb/confignode/client/handlers/HeartbeatHandler.java rename confignode/src/main/java/org/apache/iotdb/confignode/manager/{ => load}/LoadManager.java (81%) rename confignode/src/main/java/org/apache/iotdb/confignode/manager/{ => load}/allocator/CopySetRegionAllocator.java (98%) rename confignode/src/main/java/org/apache/iotdb/confignode/manager/{ => load}/allocator/IRegionAllocator.java (96%) rename confignode/src/main/java/org/apache/iotdb/confignode/manager/{ => load}/balancer/RegionBalancer.java (93%) rename confignode/src/main/java/org/apache/iotdb/confignode/manager/{ => load}/balancer/SeriesPartitionSlotBalancer.java (93%) create mode 100644 confignode/src/main/java/org/apache/iotdb/confignode/manager/load/heartbeat/HeartbeatCache.java create mode 100644 confignode/src/main/java/org/apache/iotdb/confignode/manager/load/heartbeat/HeartbeatPackage.java create mode 100644 confignode/src/main/java/org/apache/iotdb/confignode/manager/load/heartbeat/HeartbeatWindow.java create mode 100644 confignode/src/main/java/org/apache/iotdb/confignode/manager/load/heartbeat/IHeartbeatStatistic.java diff --git a/confignode/src/assembly/resources/conf/iotdb-confignode.properties b/confignode/src/assembly/resources/conf/iotdb-confignode.properties index b0fa282e918b..2ca585b2d146 100644 --- a/confignode/src/assembly/resources/conf/iotdb-confignode.properties +++ b/confignode/src/assembly/resources/conf/iotdb-confignode.properties @@ -46,6 +46,7 @@ consensus_port=22278 # Datatype: String target_confignode=0.0.0.0:22277 + #################### ### Consensus protocol configuration #################### @@ -61,6 +62,7 @@ target_confignode=0.0.0.0:22277 # Datatype: String # data_node_consensus_protocol_class=org.apache.iotdb.consensus.ratis.RatisConsensus + #################### ### PartitionSlot configuration #################### @@ -196,6 +198,7 @@ target_confignode=0.0.0.0:22277 # If its prefix is "/", then the path is absolute. Otherwise, it is relative. # consensus_dir=data/consensus + # procedure wal dir # If this property is unset, system will save the data in the default relative path directory under the confignode folder(i.e., %CONFIGNODE_HOME%/data/consensus). # If it is absolute, system will save the data in exact location it points to. @@ -207,14 +210,17 @@ target_confignode=0.0.0.0:22277 # For Linux platform # If its prefix is "/", then the path is absolute. Otherwise, it is relative. # proc_wal_dir=data/proc + #################### ### Procedure Configuration #################### + # Default number of worker thread count # Datatype: int #procedure_core_worker_thread_size=4 + # Default time interval of completed procedure cleaner work in, time unit is second # Datatype: int #procedure_completed_clean_interval=30 @@ -224,3 +230,15 @@ target_confignode=0.0.0.0:22277 # Datatype: int #procedure_completed_evict_ttl=800 +#################### +### Heartbeat configuration +#################### + + +# The heartbeat interval in milliseconds, default is 3000ms +# Datatype: long +# heartbeat_interval=3000 + + +# This parameter only exists for a few days +# enable_heartbeat=false \ No newline at end of file diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/client/AsyncDataNodeClientPool.java b/confignode/src/main/java/org/apache/iotdb/confignode/client/AsyncDataNodeClientPool.java index b4333651c175..3e156986ae22 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/client/AsyncDataNodeClientPool.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/client/AsyncDataNodeClientPool.java @@ -19,9 +19,11 @@ package org.apache.iotdb.confignode.client; import org.apache.iotdb.common.rpc.thrift.TEndPoint; +import org.apache.iotdb.common.rpc.thrift.THeartbeatReq; import org.apache.iotdb.commons.client.IClientManager; import org.apache.iotdb.commons.client.async.AsyncDataNodeInternalServiceClient; import org.apache.iotdb.confignode.client.handlers.CreateRegionHandler; +import org.apache.iotdb.confignode.client.handlers.HeartbeatHandler; import org.apache.iotdb.mpp.rpc.thrift.TCreateDataRegionReq; import org.apache.iotdb.mpp.rpc.thrift.TCreateSchemaRegionReq; @@ -81,6 +83,21 @@ public void createDataRegion( } } + /** + * Only used in LoadManager + * + * @param endPoint The specific DataNode + */ + public void getHeartBeat(TEndPoint endPoint, THeartbeatReq req, HeartbeatHandler handler) { + AsyncDataNodeInternalServiceClient client; + try { + client = clientManager.borrowClient(endPoint); + client.getHeartBeat(req, handler); + } catch (Exception e) { + LOGGER.error("Asking DataNode: {}, for heartbeat failed", endPoint, e); + } + } + /** * Always call this interface when a DataNode is restarted or removed * diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/client/SyncConfigNodeClientPool.java b/confignode/src/main/java/org/apache/iotdb/confignode/client/SyncConfigNodeClientPool.java index 329ce205d4b1..484687eacd9f 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/client/SyncConfigNodeClientPool.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/client/SyncConfigNodeClientPool.java @@ -18,11 +18,11 @@ */ package org.apache.iotdb.confignode.client; +import org.apache.iotdb.common.rpc.thrift.TConfigNodeLocation; import org.apache.iotdb.common.rpc.thrift.TEndPoint; import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.commons.client.IClientManager; import org.apache.iotdb.commons.client.sync.SyncConfigNodeIServiceClient; -import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeLocation; import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeRegisterReq; import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeRegisterResp; import org.apache.iotdb.db.client.DataNodeClientPoolFactory; diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/client/handlers/HeartbeatHandler.java b/confignode/src/main/java/org/apache/iotdb/confignode/client/handlers/HeartbeatHandler.java new file mode 100644 index 000000000000..84b8f2ec71d0 --- /dev/null +++ b/confignode/src/main/java/org/apache/iotdb/confignode/client/handlers/HeartbeatHandler.java @@ -0,0 +1,49 @@ +/* + * 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.iotdb.confignode.client.handlers; + +import org.apache.iotdb.common.rpc.thrift.THeartbeatResp; +import org.apache.iotdb.confignode.manager.load.heartbeat.HeartbeatCache; +import org.apache.iotdb.confignode.manager.load.heartbeat.HeartbeatPackage; + +import org.apache.thrift.async.AsyncMethodCallback; + +public class HeartbeatHandler implements AsyncMethodCallback { + + // Update HeartbeatCache when success + private final int dataNodeId; + private final HeartbeatCache heartbeatCache; + + public HeartbeatHandler(int dataNodeId, HeartbeatCache heartbeatCache) { + this.dataNodeId = dataNodeId; + this.heartbeatCache = heartbeatCache; + } + + @Override + public void onComplete(THeartbeatResp tHeartbeatResp) { + heartbeatCache.cacheHeartBeat( + dataNodeId, + new HeartbeatPackage(tHeartbeatResp.getHeartbeatTimestamp(), System.currentTimeMillis())); + } + + @Override + public void onError(Exception e) { + // Just ignore heartbeat error + } +} diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/conf/ConfigNodeConf.java b/confignode/src/main/java/org/apache/iotdb/confignode/conf/ConfigNodeConf.java index 96c481b02750..923fe5c7003c 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/conf/ConfigNodeConf.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/conf/ConfigNodeConf.java @@ -18,9 +18,9 @@ */ package org.apache.iotdb.confignode.conf; +import org.apache.iotdb.common.rpc.thrift.TConfigNodeLocation; import org.apache.iotdb.common.rpc.thrift.TEndPoint; import org.apache.iotdb.commons.conf.IoTDBConstant; -import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeLocation; import org.apache.iotdb.rpc.RpcUtils; import java.io.File; @@ -128,6 +128,12 @@ public class ConfigNodeConf { private int procedureCoreWorkerThreadsSize = Math.max(Runtime.getRuntime().availableProcessors() / 4, 16); + /** The heartbeat interval in milliseconds */ + private long heartbeatInterval = 3000; + + /** This parameter only exists for a few days */ + private boolean enableHeartbeat = false; + ConfigNodeConf() { // empty constructor } @@ -381,4 +387,20 @@ public int getProcedureCoreWorkerThreadsSize() { public void setProcedureCoreWorkerThreadsSize(int procedureCoreWorkerThreadsSize) { this.procedureCoreWorkerThreadsSize = procedureCoreWorkerThreadsSize; } + + public long getHeartbeatInterval() { + return heartbeatInterval; + } + + public void setHeartbeatInterval(long heartbeatInterval) { + this.heartbeatInterval = heartbeatInterval; + } + + public boolean isEnableHeartbeat() { + return enableHeartbeat; + } + + public void setEnableHeartbeat(boolean enableHeartbeat) { + this.enableHeartbeat = enableHeartbeat; + } } diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/conf/ConfigNodeDescriptor.java b/confignode/src/main/java/org/apache/iotdb/confignode/conf/ConfigNodeDescriptor.java index b1c4e49328fd..240021ae40fc 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/conf/ConfigNodeDescriptor.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/conf/ConfigNodeDescriptor.java @@ -202,6 +202,16 @@ private void loadProps() { properties.getProperty( "maximum_data_region_count", String.valueOf(conf.getMaximumDataRegionCount())))); + conf.setHeartbeatInterval( + Long.parseLong( + properties.getProperty( + "heartbeat_interval", String.valueOf(conf.getHeartbeatInterval())))); + + conf.setEnableHeartbeat( + Boolean.parseBoolean( + properties.getProperty( + "enable_heartbeat", String.valueOf(conf.isEnableHeartbeat())))); + // commons commonDescriptor.loadCommonProps(properties); commonDescriptor.initCommonConfigDir(conf.getSystemDir()); diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/conf/ConfigNodeStartupCheck.java b/confignode/src/main/java/org/apache/iotdb/confignode/conf/ConfigNodeStartupCheck.java index ca4713adf34a..7480d78e0cff 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/conf/ConfigNodeStartupCheck.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/conf/ConfigNodeStartupCheck.java @@ -18,6 +18,7 @@ */ package org.apache.iotdb.confignode.conf; +import org.apache.iotdb.common.rpc.thrift.TConfigNodeLocation; import org.apache.iotdb.common.rpc.thrift.TEndPoint; import org.apache.iotdb.commons.conf.CommonDescriptor; import org.apache.iotdb.commons.exception.BadNodeUrlException; @@ -25,7 +26,6 @@ import org.apache.iotdb.commons.exception.StartupException; import org.apache.iotdb.commons.utils.NodeUrlUtils; import org.apache.iotdb.confignode.client.SyncConfigNodeClientPool; -import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeLocation; import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeRegisterReq; import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeRegisterResp; import org.apache.iotdb.rpc.TSStatusCode; diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/ApplyConfigNodeReq.java b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/ApplyConfigNodeReq.java index 0a00beff6e01..3eeceae8b74e 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/ApplyConfigNodeReq.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/ApplyConfigNodeReq.java @@ -18,10 +18,10 @@ */ package org.apache.iotdb.confignode.consensus.request.write; +import org.apache.iotdb.common.rpc.thrift.TConfigNodeLocation; import org.apache.iotdb.commons.utils.ThriftConfigNodeSerDeUtils; import org.apache.iotdb.confignode.consensus.request.ConfigRequest; import org.apache.iotdb.confignode.consensus.request.ConfigRequestType; -import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeLocation; import java.io.IOException; import java.nio.ByteBuffer; diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/DataNodeConfigurationResp.java b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/DataNodeConfigurationResp.java index f21371b46588..c4672cf4b0ac 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/DataNodeConfigurationResp.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/DataNodeConfigurationResp.java @@ -18,8 +18,8 @@ */ package org.apache.iotdb.confignode.consensus.response; +import org.apache.iotdb.common.rpc.thrift.TConfigNodeLocation; import org.apache.iotdb.common.rpc.thrift.TSStatus; -import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeLocation; import org.apache.iotdb.confignode.rpc.thrift.TDataNodeRegisterResp; import org.apache.iotdb.confignode.rpc.thrift.TGlobalConfig; import org.apache.iotdb.consensus.common.DataSet; diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java index 47ebb6703468..278859b9c45b 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java @@ -48,6 +48,7 @@ import org.apache.iotdb.confignode.consensus.response.PermissionInfoResp; import org.apache.iotdb.confignode.consensus.response.SchemaPartitionResp; import org.apache.iotdb.confignode.consensus.response.StorageGroupSchemaResp; +import org.apache.iotdb.confignode.manager.load.LoadManager; import org.apache.iotdb.confignode.persistence.ClusterSchemaInfo; import org.apache.iotdb.confignode.persistence.NodeInfo; import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeRegisterReq; @@ -101,6 +102,12 @@ public ConfigManager() throws IOException { this.loadManager = new LoadManager(this); this.procedureManager = new ProcedureManager(this); this.consensusManager = new ConsensusManager(this); + + // We are on testing....... + if (ConfigNodeDescriptor.getInstance().getConf().isEnableHeartbeat()) { + // Start asking for heartbeat + new Thread(this.loadManager).start(); + } } public void close() throws IOException { diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConsensusManager.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConsensusManager.java index 8d093e326c48..80064c33fc75 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConsensusManager.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConsensusManager.java @@ -18,6 +18,7 @@ */ package org.apache.iotdb.confignode.manager; +import org.apache.iotdb.common.rpc.thrift.TConfigNodeLocation; import org.apache.iotdb.common.rpc.thrift.TEndPoint; import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.commons.consensus.ConsensusGroupId; @@ -28,7 +29,6 @@ import org.apache.iotdb.confignode.consensus.request.ConfigRequest; import org.apache.iotdb.confignode.consensus.request.write.ApplyConfigNodeReq; import org.apache.iotdb.confignode.consensus.statemachine.PartitionRegionStateMachine; -import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeLocation; import org.apache.iotdb.consensus.ConsensusFactory; import org.apache.iotdb.consensus.IConsensus; import org.apache.iotdb.consensus.common.Peer; @@ -49,7 +49,7 @@ public class ConsensusManager { private static final Logger LOGGER = LoggerFactory.getLogger(ConsensusManager.class); private static final ConfigNodeConf conf = ConfigNodeDescriptor.getInstance().getConf(); - private ConfigManager configManager; + private final ConfigManager configManager; private ConsensusGroupId consensusGroupId; private IConsensus consensusImpl; diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/Manager.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/Manager.java index 32020b0575f9..6a7fc0a35fef 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/Manager.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/Manager.java @@ -32,6 +32,7 @@ import org.apache.iotdb.confignode.consensus.request.write.SetStorageGroupReq; import org.apache.iotdb.confignode.consensus.request.write.SetTTLReq; import org.apache.iotdb.confignode.consensus.request.write.SetTimePartitionIntervalReq; +import org.apache.iotdb.confignode.manager.load.LoadManager; import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeRegisterReq; import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeRegisterResp; import org.apache.iotdb.consensus.common.DataSet; diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/PartitionManager.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/PartitionManager.java index e9ec2470070f..f0db7b0d0522 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/PartitionManager.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/PartitionManager.java @@ -37,6 +37,7 @@ import org.apache.iotdb.confignode.consensus.response.DataPartitionResp; import org.apache.iotdb.confignode.consensus.response.SchemaPartitionResp; import org.apache.iotdb.confignode.exception.NotEnoughDataNodeException; +import org.apache.iotdb.confignode.manager.load.LoadManager; import org.apache.iotdb.confignode.persistence.PartitionInfo; import org.apache.iotdb.consensus.common.DataSet; import org.apache.iotdb.consensus.common.response.ConsensusReadResponse; diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/LoadManager.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/LoadManager.java similarity index 81% rename from confignode/src/main/java/org/apache/iotdb/confignode/manager/LoadManager.java rename to confignode/src/main/java/org/apache/iotdb/confignode/manager/load/LoadManager.java index 67aa1e7ffb9f..cae71a74e84b 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/LoadManager.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/LoadManager.java @@ -16,20 +16,29 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.iotdb.confignode.manager; +package org.apache.iotdb.confignode.manager.load; import org.apache.iotdb.common.rpc.thrift.TConsensusGroupId; import org.apache.iotdb.common.rpc.thrift.TConsensusGroupType; import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation; import org.apache.iotdb.common.rpc.thrift.TEndPoint; +import org.apache.iotdb.common.rpc.thrift.THeartbeatReq; import org.apache.iotdb.common.rpc.thrift.TRegionReplicaSet; import org.apache.iotdb.commons.exception.MetadataException; import org.apache.iotdb.confignode.client.AsyncDataNodeClientPool; import org.apache.iotdb.confignode.client.handlers.CreateRegionHandler; +import org.apache.iotdb.confignode.client.handlers.HeartbeatHandler; +import org.apache.iotdb.confignode.conf.ConfigNodeDescriptor; import org.apache.iotdb.confignode.consensus.request.write.CreateRegionsReq; import org.apache.iotdb.confignode.exception.NotEnoughDataNodeException; -import org.apache.iotdb.confignode.manager.allocator.CopySetRegionAllocator; -import org.apache.iotdb.confignode.manager.allocator.IRegionAllocator; +import org.apache.iotdb.confignode.manager.ClusterSchemaManager; +import org.apache.iotdb.confignode.manager.ConsensusManager; +import org.apache.iotdb.confignode.manager.Manager; +import org.apache.iotdb.confignode.manager.NodeManager; +import org.apache.iotdb.confignode.manager.PartitionManager; +import org.apache.iotdb.confignode.manager.load.allocator.CopySetRegionAllocator; +import org.apache.iotdb.confignode.manager.load.allocator.IRegionAllocator; +import org.apache.iotdb.confignode.manager.load.heartbeat.HeartbeatCache; import org.apache.iotdb.confignode.rpc.thrift.TStorageGroupSchema; import org.apache.iotdb.mpp.rpc.thrift.TCreateDataRegionReq; import org.apache.iotdb.mpp.rpc.thrift.TCreateSchemaRegionReq; @@ -42,23 +51,30 @@ import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; /** * The LoadManager at ConfigNodeGroup-Leader is active. It proactively implements the cluster * dynamic load balancing policy and passively accepts the PartitionTable expansion request. */ -public class LoadManager { +public class LoadManager implements Runnable { private static final Logger LOGGER = LoggerFactory.getLogger(LoadManager.class); + private final long heartbeatInterval = + ConfigNodeDescriptor.getInstance().getConf().getHeartbeatInterval(); + private final Manager configManager; + private final HeartbeatCache heartbeatCache; + private final IRegionAllocator regionAllocator; // TODO: Interfaces for active, interrupt and reset LoadBalancer public LoadManager(Manager configManager) { this.configManager = configManager; + this.heartbeatCache = new HeartbeatCache(); this.regionAllocator = new CopySetRegionAllocator(); } @@ -237,4 +253,35 @@ private ClusterSchemaManager getClusterSchemaManager() { private PartitionManager getPartitionManager() { return configManager.getPartitionManager(); } + + private THeartbeatReq genHeartbeatReq() { + return new THeartbeatReq(System.currentTimeMillis()); + } + + @Override + public void run() { + while (true) { + try { + + if (getConsensusManager().isLeader()) { + // Ask DataNode for heartbeat in every heartbeat interval + List onlineDataNodes = getNodeManager().getOnlineDataNodes(); + for (TDataNodeLocation dataNodeLocation : onlineDataNodes) { + HeartbeatHandler handler = + new HeartbeatHandler(dataNodeLocation.getDataNodeId(), heartbeatCache); + AsyncDataNodeClientPool.getInstance() + .getHeartBeat(dataNodeLocation.getInternalEndPoint(), genHeartbeatReq(), handler); + } + + } else { + heartbeatCache.discardAllCache(); + } + + TimeUnit.MILLISECONDS.sleep(heartbeatInterval); + } catch (InterruptedException e) { + LOGGER.error("Heartbeat thread has been interrupted, stopping ConfigNode...", e); + System.exit(-1); + } + } + } } diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/allocator/CopySetRegionAllocator.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/allocator/CopySetRegionAllocator.java similarity index 98% rename from confignode/src/main/java/org/apache/iotdb/confignode/manager/allocator/CopySetRegionAllocator.java rename to confignode/src/main/java/org/apache/iotdb/confignode/manager/load/allocator/CopySetRegionAllocator.java index 00da9dc05f6d..7cdc8adf153d 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/allocator/CopySetRegionAllocator.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/allocator/CopySetRegionAllocator.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.iotdb.confignode.manager.allocator; +package org.apache.iotdb.confignode.manager.load.allocator; import org.apache.iotdb.common.rpc.thrift.TConsensusGroupId; import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation; diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/allocator/IRegionAllocator.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/allocator/IRegionAllocator.java similarity index 96% rename from confignode/src/main/java/org/apache/iotdb/confignode/manager/allocator/IRegionAllocator.java rename to confignode/src/main/java/org/apache/iotdb/confignode/manager/load/allocator/IRegionAllocator.java index 4874f5a18c8a..1466fd5d569f 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/allocator/IRegionAllocator.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/allocator/IRegionAllocator.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.iotdb.confignode.manager.allocator; +package org.apache.iotdb.confignode.manager.load.allocator; import org.apache.iotdb.common.rpc.thrift.TConsensusGroupId; import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation; diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/balancer/RegionBalancer.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/balancer/RegionBalancer.java similarity index 93% rename from confignode/src/main/java/org/apache/iotdb/confignode/manager/balancer/RegionBalancer.java rename to confignode/src/main/java/org/apache/iotdb/confignode/manager/load/balancer/RegionBalancer.java index bf7e24527bb5..2e099883d292 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/balancer/RegionBalancer.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/balancer/RegionBalancer.java @@ -16,6 +16,6 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.iotdb.confignode.manager.balancer; +package org.apache.iotdb.confignode.manager.load.balancer; public class RegionBalancer {} diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/balancer/SeriesPartitionSlotBalancer.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/balancer/SeriesPartitionSlotBalancer.java similarity index 93% rename from confignode/src/main/java/org/apache/iotdb/confignode/manager/balancer/SeriesPartitionSlotBalancer.java rename to confignode/src/main/java/org/apache/iotdb/confignode/manager/load/balancer/SeriesPartitionSlotBalancer.java index 441f732264e7..99225af381af 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/balancer/SeriesPartitionSlotBalancer.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/balancer/SeriesPartitionSlotBalancer.java @@ -16,6 +16,6 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.iotdb.confignode.manager.balancer; +package org.apache.iotdb.confignode.manager.load.balancer; public class SeriesPartitionSlotBalancer {} diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/heartbeat/HeartbeatCache.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/heartbeat/HeartbeatCache.java new file mode 100644 index 000000000000..29a29ff4f80c --- /dev/null +++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/heartbeat/HeartbeatCache.java @@ -0,0 +1,51 @@ +/* + * 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.iotdb.confignode.manager.load.heartbeat; + +import java.util.HashMap; +import java.util.Map; + +/** HeartbeatCache caches and maintains all the heartbeat data */ +public class HeartbeatCache implements IHeartbeatStatistic { + + private boolean containsCache = false; + + // Map + private final Map windowMap; + + public HeartbeatCache() { + this.windowMap = new HashMap<>(); + } + + @Override + public void cacheHeartBeat(int dataNodeId, HeartbeatPackage newHeartbeat) { + containsCache = true; + windowMap + .computeIfAbsent(dataNodeId, window -> new HeartbeatWindow()) + .addHeartbeat(newHeartbeat); + } + + @Override + public void discardAllCache() { + if (containsCache) { + containsCache = false; + windowMap.clear(); + } + } +} diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/heartbeat/HeartbeatPackage.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/heartbeat/HeartbeatPackage.java new file mode 100644 index 000000000000..b7bbcb9bbb6c --- /dev/null +++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/heartbeat/HeartbeatPackage.java @@ -0,0 +1,38 @@ +/* + * 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.iotdb.confignode.manager.load.heartbeat; + +public class HeartbeatPackage { + + private final long sendTimestamp; + private final long receiveTimestamp; + + public HeartbeatPackage(long sendTimestamp, long receiveTimestamp) { + this.sendTimestamp = sendTimestamp; + this.receiveTimestamp = receiveTimestamp; + } + + public long getSendTimestamp() { + return sendTimestamp; + } + + public long getReceiveTimestamp() { + return receiveTimestamp; + } +} diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/heartbeat/HeartbeatWindow.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/heartbeat/HeartbeatWindow.java new file mode 100644 index 000000000000..6478b10a1a9b --- /dev/null +++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/heartbeat/HeartbeatWindow.java @@ -0,0 +1,51 @@ +/* + * 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.iotdb.confignode.manager.load.heartbeat; + +import java.util.LinkedList; + +/** + * HeartbeatWindow contains Heartbeat's sending and receiving time, which is used for estimating + * when the next heartbeat will arrive. + */ +public class HeartbeatWindow { + + private static final int maximumWindowSize = 1000; + + private final LinkedList slidingWindow; + + public HeartbeatWindow() { + this.slidingWindow = new LinkedList<>(); + } + + public void addHeartbeat(HeartbeatPackage newHeartbeat) { + synchronized (slidingWindow) { + // Only sequential heartbeats are accepted. + // And un-sequential heartbeats will be discarded. + if (slidingWindow.size() == 0 + || slidingWindow.getLast().getSendTimestamp() < newHeartbeat.getSendTimestamp()) { + slidingWindow.add(newHeartbeat); + } + + while (slidingWindow.size() > maximumWindowSize) { + slidingWindow.removeFirst(); + } + } + } +} diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/heartbeat/IHeartbeatStatistic.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/heartbeat/IHeartbeatStatistic.java new file mode 100644 index 000000000000..9cfdb0890df1 --- /dev/null +++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/heartbeat/IHeartbeatStatistic.java @@ -0,0 +1,36 @@ +/* + * 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.iotdb.confignode.manager.load.heartbeat; + +/** All the interfaces that provided by HeartbeatCache */ +public interface IHeartbeatStatistic { + + /** + * Cache the newest HeartbeatData of the specific DataNode + * + * @param dataNodeId The specific DataNodeId + * @param newHeartbeat The newest HeartbeatData + */ + void cacheHeartBeat(int dataNodeId, HeartbeatPackage newHeartbeat); + + // TODO: Interfaces for statistics + + /** Only use this interface when current ConfigNode is not the leader */ + void discardAllCache(); +} diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/NodeInfo.java b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/NodeInfo.java index 38af6e207f85..308090848ca7 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/NodeInfo.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/NodeInfo.java @@ -18,6 +18,7 @@ */ package org.apache.iotdb.confignode.persistence; +import org.apache.iotdb.common.rpc.thrift.TConfigNodeLocation; import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation; import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.commons.utils.NodeUrlUtils; @@ -28,7 +29,6 @@ import org.apache.iotdb.confignode.consensus.request.write.ApplyConfigNodeReq; import org.apache.iotdb.confignode.consensus.request.write.RegisterDataNodeReq; import org.apache.iotdb.confignode.consensus.response.DataNodeLocationsResp; -import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeLocation; import org.apache.iotdb.rpc.TSStatusCode; import org.slf4j.Logger; diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessor.java b/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessor.java index 2abff56aaac5..08795217ac1c 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessor.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessor.java @@ -18,6 +18,7 @@ */ package org.apache.iotdb.confignode.service.thrift; +import org.apache.iotdb.common.rpc.thrift.TConfigNodeLocation; import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.commons.auth.AuthException; import org.apache.iotdb.commons.conf.CommonDescriptor; @@ -49,7 +50,6 @@ import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerReq; import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerResp; import org.apache.iotdb.confignode.rpc.thrift.TCheckUserPrivilegesReq; -import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeLocation; import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeRegisterReq; import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeRegisterResp; import org.apache.iotdb.confignode.rpc.thrift.TCountStorageGroupResp; diff --git a/confignode/src/test/java/org/apache/iotdb/confignode/consensus/request/ConfigRequestSerDeTest.java b/confignode/src/test/java/org/apache/iotdb/confignode/consensus/request/ConfigRequestSerDeTest.java index 9f6e10a14ceb..89f9cdfefade 100644 --- a/confignode/src/test/java/org/apache/iotdb/confignode/consensus/request/ConfigRequestSerDeTest.java +++ b/confignode/src/test/java/org/apache/iotdb/confignode/consensus/request/ConfigRequestSerDeTest.java @@ -18,6 +18,7 @@ */ package org.apache.iotdb.confignode.consensus.request; +import org.apache.iotdb.common.rpc.thrift.TConfigNodeLocation; import org.apache.iotdb.common.rpc.thrift.TConsensusGroupId; import org.apache.iotdb.common.rpc.thrift.TConsensusGroupType; import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation; @@ -50,7 +51,6 @@ import org.apache.iotdb.confignode.consensus.request.write.SetTimePartitionIntervalReq; import org.apache.iotdb.confignode.consensus.request.write.UpdateProcedureReq; import org.apache.iotdb.confignode.procedure.DeleteStorageGroupProcedure; -import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeLocation; import org.apache.iotdb.confignode.rpc.thrift.TStorageGroupSchema; import org.apache.iotdb.procedure.Procedure; diff --git a/node-commons/src/main/java/org/apache/iotdb/commons/utils/NodeUrlUtils.java b/node-commons/src/main/java/org/apache/iotdb/commons/utils/NodeUrlUtils.java index ed76fabcc317..a502e4d42ee4 100644 --- a/node-commons/src/main/java/org/apache/iotdb/commons/utils/NodeUrlUtils.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/utils/NodeUrlUtils.java @@ -19,9 +19,9 @@ package org.apache.iotdb.commons.utils; +import org.apache.iotdb.common.rpc.thrift.TConfigNodeLocation; import org.apache.iotdb.common.rpc.thrift.TEndPoint; import org.apache.iotdb.commons.exception.BadNodeUrlException; -import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeLocation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/node-commons/src/main/java/org/apache/iotdb/commons/utils/ThriftConfigNodeSerDeUtils.java b/node-commons/src/main/java/org/apache/iotdb/commons/utils/ThriftConfigNodeSerDeUtils.java index ce057376085b..2b7bef298620 100644 --- a/node-commons/src/main/java/org/apache/iotdb/commons/utils/ThriftConfigNodeSerDeUtils.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/utils/ThriftConfigNodeSerDeUtils.java @@ -18,8 +18,8 @@ */ package org.apache.iotdb.commons.utils; +import org.apache.iotdb.common.rpc.thrift.TConfigNodeLocation; import org.apache.iotdb.commons.exception.runtime.ThriftSerDeException; -import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeLocation; import org.apache.iotdb.confignode.rpc.thrift.TStorageGroupSchema; import org.apache.thrift.TException; diff --git a/node-commons/src/test/java/org/apache/iotdb/commons/utils/NodeUrlUtilsTest.java b/node-commons/src/test/java/org/apache/iotdb/commons/utils/NodeUrlUtilsTest.java index 3c7667eea441..f6e497f3096e 100644 --- a/node-commons/src/test/java/org/apache/iotdb/commons/utils/NodeUrlUtilsTest.java +++ b/node-commons/src/test/java/org/apache/iotdb/commons/utils/NodeUrlUtilsTest.java @@ -18,9 +18,9 @@ */ package org.apache.iotdb.commons.utils; +import org.apache.iotdb.common.rpc.thrift.TConfigNodeLocation; import org.apache.iotdb.common.rpc.thrift.TEndPoint; import org.apache.iotdb.commons.exception.BadNodeUrlException; -import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeLocation; import org.junit.Assert; import org.junit.Test; diff --git a/node-commons/src/test/java/org/apache/iotdb/commons/utils/ThriftConfigNodeSerDeUtilsTest.java b/node-commons/src/test/java/org/apache/iotdb/commons/utils/ThriftConfigNodeSerDeUtilsTest.java index 1f150fd2fddd..60e0adef7b05 100644 --- a/node-commons/src/test/java/org/apache/iotdb/commons/utils/ThriftConfigNodeSerDeUtilsTest.java +++ b/node-commons/src/test/java/org/apache/iotdb/commons/utils/ThriftConfigNodeSerDeUtilsTest.java @@ -18,10 +18,10 @@ */ package org.apache.iotdb.commons.utils; +import org.apache.iotdb.common.rpc.thrift.TConfigNodeLocation; import org.apache.iotdb.common.rpc.thrift.TConsensusGroupId; import org.apache.iotdb.common.rpc.thrift.TConsensusGroupType; import org.apache.iotdb.common.rpc.thrift.TEndPoint; -import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeLocation; import org.apache.iotdb.confignode.rpc.thrift.TStorageGroupSchema; import org.junit.After; diff --git a/server/src/main/java/org/apache/iotdb/db/client/ConfigNodeClient.java b/server/src/main/java/org/apache/iotdb/db/client/ConfigNodeClient.java index 50dd5f474160..ecff7dbcd35e 100644 --- a/server/src/main/java/org/apache/iotdb/db/client/ConfigNodeClient.java +++ b/server/src/main/java/org/apache/iotdb/db/client/ConfigNodeClient.java @@ -19,13 +19,13 @@ package org.apache.iotdb.db.client; +import org.apache.iotdb.common.rpc.thrift.TConfigNodeLocation; import org.apache.iotdb.common.rpc.thrift.TEndPoint; import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.confignode.rpc.thrift.ConfigIService; import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerReq; import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerResp; import org.apache.iotdb.confignode.rpc.thrift.TCheckUserPrivilegesReq; -import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeLocation; import org.apache.iotdb.confignode.rpc.thrift.TCountStorageGroupResp; import org.apache.iotdb.confignode.rpc.thrift.TDataNodeLocationResp; import org.apache.iotdb.confignode.rpc.thrift.TDataNodeRegisterReq; diff --git a/server/src/main/java/org/apache/iotdb/db/service/DataNode.java b/server/src/main/java/org/apache/iotdb/db/service/DataNode.java index 61b764e2eb6f..887515016240 100644 --- a/server/src/main/java/org/apache/iotdb/db/service/DataNode.java +++ b/server/src/main/java/org/apache/iotdb/db/service/DataNode.java @@ -18,6 +18,7 @@ */ package org.apache.iotdb.db.service; +import org.apache.iotdb.common.rpc.thrift.TConfigNodeLocation; import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation; import org.apache.iotdb.common.rpc.thrift.TEndPoint; import org.apache.iotdb.commons.concurrent.IoTDBDefaultThreadExceptionHandler; @@ -27,7 +28,6 @@ import org.apache.iotdb.commons.service.JMXService; import org.apache.iotdb.commons.service.RegisterManager; import org.apache.iotdb.commons.service.StartupChecks; -import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeLocation; import org.apache.iotdb.confignode.rpc.thrift.TDataNodeRegisterReq; import org.apache.iotdb.confignode.rpc.thrift.TDataNodeRegisterResp; import org.apache.iotdb.db.client.ConfigNodeClient; diff --git a/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/InternalServiceImpl.java b/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/InternalServiceImpl.java index b581cedfe150..1a4661d5b4db 100644 --- a/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/InternalServiceImpl.java +++ b/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/InternalServiceImpl.java @@ -22,6 +22,8 @@ import org.apache.iotdb.common.rpc.thrift.TConsensusGroupId; import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation; import org.apache.iotdb.common.rpc.thrift.TEndPoint; +import org.apache.iotdb.common.rpc.thrift.THeartbeatReq; +import org.apache.iotdb.common.rpc.thrift.THeartbeatResp; import org.apache.iotdb.common.rpc.thrift.TRegionReplicaSet; import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.commons.consensus.ConsensusGroupId; @@ -262,6 +264,12 @@ public TSStatus migrateDataRegion(TMigrateDataRegionReq req) throws TException { return null; } + @Override + public THeartbeatResp getHeartBeat(THeartbeatReq req) throws TException { + // TODO: Return load balancing messages + return new THeartbeatResp(req.getHeartbeatTimestamp()); + } + @Override public TSStatus deleteRegion(TConsensusGroupId tconsensusGroupId) throws TException { long queryIdRaw = SessionManager.getInstance().requestQueryId(false); diff --git a/thrift-commons/src/main/thrift/common.thrift b/thrift-commons/src/main/thrift/common.thrift index 9b09bc37436b..8ffdf3ff4048 100644 --- a/thrift-commons/src/main/thrift/common.thrift +++ b/thrift-commons/src/main/thrift/common.thrift @@ -57,6 +57,15 @@ struct TRegionReplicaSet { 2: required list dataNodeLocations } +struct TConfigNodeLocation { + 1: required TEndPoint internalEndPoint + 2: required TEndPoint consensusEndPoint +} + +struct THeartbeatReq { + 1: required i64 heartbeatTimestamp +} + struct TDataNodeLocation { 1: required i32 dataNodeId // TEndPoint for DataNode's external rpc @@ -67,4 +76,8 @@ struct TDataNodeLocation { 4: required TEndPoint dataBlockManagerEndPoint // TEndPoint for DataNode's ConsensusLayer 5: required TEndPoint consensusEndPoint +} + +struct THeartbeatResp { + 1: required i64 heartbeatTimestamp } \ No newline at end of file diff --git a/thrift-confignode/src/main/thrift/confignode.thrift b/thrift-confignode/src/main/thrift/confignode.thrift index 653d9ac2d7cd..7fa76108e819 100644 --- a/thrift-confignode/src/main/thrift/confignode.thrift +++ b/thrift-confignode/src/main/thrift/confignode.thrift @@ -38,7 +38,7 @@ struct TGlobalConfig { struct TDataNodeRegisterResp { 1: required common.TSStatus status - 2: required list configNodeList + 2: required list configNodeList 3: optional i32 dataNodeId 4: optional TGlobalConfig globalConfig } @@ -157,13 +157,8 @@ struct TCheckUserPrivilegesReq{ } // ConfigNode -struct TConfigNodeLocation { - 1: required common.TEndPoint internalEndPoint - 2: required common.TEndPoint consensusEndPoint -} - struct TConfigNodeRegisterReq { - 1: required TConfigNodeLocation configNodeLocation + 1: required common.TConfigNodeLocation configNodeLocation 2: required string dataNodeConsensusProtocolClass 3: required i32 seriesPartitionSlotNum 4: required string seriesPartitionExecutorClass @@ -176,7 +171,7 @@ struct TConfigNodeRegisterReq { struct TConfigNodeRegisterResp { 1: required common.TSStatus status 2: optional common.TConsensusGroupId partitionRegionId - 3: optional list configNodeList + 3: optional list configNodeList } service ConfigIService { @@ -233,5 +228,5 @@ service ConfigIService { TConfigNodeRegisterResp registerConfigNode(TConfigNodeRegisterReq req) - common.TSStatus applyConfigNode(TConfigNodeLocation configNodeLocation) + common.TSStatus applyConfigNode(common.TConfigNodeLocation configNodeLocation) } \ No newline at end of file diff --git a/thrift/src/main/thrift/mpp.thrift b/thrift/src/main/thrift/mpp.thrift index 51be9a80b308..dfc26e4da00c 100644 --- a/thrift/src/main/thrift/mpp.thrift +++ b/thrift/src/main/thrift/mpp.thrift @@ -204,6 +204,12 @@ service InternalService { */ common.TSStatus migrateDataRegion(TMigrateDataRegionReq req) + /** + * ConfigNode will ask DataNode for heartbeat in every few seconds. + * + * @param ConfigNode will send the latest config_node_list and load balancing policies in THeartbeatReq + **/ + common.THeartbeatResp getHeartBeat(common.THeartbeatReq req) } service DataBlockService { From 214552221364ff474bc2ac18e67fe95670cdc7d5 Mon Sep 17 00:00:00 2001 From: lisijia <44458757+cigarl@users.noreply.github.com> Date: Fri, 13 May 2022 23:49:48 +0800 Subject: [PATCH 004/436] [IOTDB-3114] NodeInfo snapshot interface (#5887) --- .../confignode/persistence/NodeInfo.java | 137 ++++++++++++++++-- .../executor/ConfigRequestExecutor.java | 1 + .../confignode/persistence/NodeInfoTest.java | 102 +++++++++++++ 3 files changed, 231 insertions(+), 9 deletions(-) create mode 100644 confignode/src/test/java/org/apache/iotdb/confignode/persistence/NodeInfoTest.java diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/NodeInfo.java b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/NodeInfo.java index 308090848ca7..818cf7adedab 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/NodeInfo.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/NodeInfo.java @@ -31,22 +31,29 @@ import org.apache.iotdb.confignode.consensus.response.DataNodeLocationsResp; import org.apache.iotdb.rpc.TSStatusCode; +import org.apache.thrift.TException; +import org.apache.thrift.protocol.TBinaryProtocol; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.transport.TIOStreamTransport; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.DataInputStream; +import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; -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.Map.Entry; import java.util.Properties; import java.util.Set; +import java.util.UUID; import java.util.concurrent.ConcurrentNavigableMap; import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.atomic.AtomicInteger; @@ -56,7 +63,7 @@ * The NodeInfo stores cluster node information. The cluster node information including: 1. DataNode * information 2. ConfigNode information */ -public class NodeInfo { +public class NodeInfo implements SnapshotProcessor { private static final Logger LOGGER = LoggerFactory.getLogger(NodeInfo.class); @@ -78,19 +85,18 @@ public class NodeInfo { private final ReentrantReadWriteLock dataNodeInfoReadWriteLock; - // TODO: serialize and deserialize private AtomicInteger nextDataNodeId = new AtomicInteger(0); // Online DataNodes - // TODO: serialize and deserialize private final ConcurrentNavigableMap onlineDataNodes = new ConcurrentSkipListMap(); // For remove or draining DataNode // TODO: implement - // TODO: serialize and deserialize private final Set drainingDataNodes = new HashSet<>(); + private final String snapshotFileName = "node_info.bin"; + private NodeInfo() { this.dataNodeInfoReadWriteLock = new ReentrantReadWriteLock(); this.configNodeInfoReadWriteLock = new ReentrantReadWriteLock(); @@ -272,15 +278,128 @@ public int generateNextDataNodeId() { return nextDataNodeId.getAndIncrement(); } - public void serialize(ByteBuffer buffer) { - // TODO: Serialize DataNodeInfo + @Override + public boolean processTakeSnapshot(File snapshotDir) throws IOException, TException { + File snapshotFile = new File(snapshotDir, snapshotFileName); + if (snapshotFile.exists() && snapshotFile.isFile()) { + LOGGER.error( + "Failed to take snapshot, because snapshot file [{}] is already exist.", + snapshotFile.getAbsolutePath()); + return false; + } + + File tmpFile = new File(snapshotFile.getAbsolutePath() + "-" + UUID.randomUUID()); + configNodeInfoReadWriteLock.readLock().lock(); + dataNodeInfoReadWriteLock.readLock().lock(); + try (FileOutputStream fileOutputStream = new FileOutputStream(tmpFile); + DataOutputStream dataOutputStream = new DataOutputStream(fileOutputStream); + TIOStreamTransport tioStreamTransport = new TIOStreamTransport(dataOutputStream)) { + + TProtocol protocol = new TBinaryProtocol(tioStreamTransport); + + dataOutputStream.writeInt(nextDataNodeId.get()); + + serializeOnlineDataNode(dataOutputStream, protocol); + + serializeDrainingDataNodes(dataOutputStream, protocol); + + fileOutputStream.flush(); + } finally { + configNodeInfoReadWriteLock.readLock().unlock(); + dataNodeInfoReadWriteLock.readLock().unlock(); + } + + return tmpFile.renameTo(snapshotFile); + } + + private void serializeOnlineDataNode(DataOutputStream outputStream, TProtocol protocol) + throws IOException, TException { + outputStream.writeInt(onlineDataNodes.size()); + for (Entry entry : onlineDataNodes.entrySet()) { + outputStream.writeInt(entry.getKey()); + entry.getValue().write(protocol); + } } - public void deserialize(ByteBuffer buffer) { - // TODO: Deserialize DataNodeInfo + private void serializeDrainingDataNodes(DataOutputStream outputStream, TProtocol protocol) + throws IOException, TException { + outputStream.writeInt(drainingDataNodes.size()); + for (TDataNodeLocation tDataNodeLocation : drainingDataNodes) { + tDataNodeLocation.write(protocol); + } + } + + @Override + public void processLoadSnapshot(File snapshotDir) throws IOException, TException { + + File snapshotFile = new File(snapshotDir, snapshotFileName); + if (!snapshotFile.exists() || !snapshotFile.isFile()) { + LOGGER.error( + "Failed to load snapshot,snapshot file [{}] is not exist.", + snapshotFile.getAbsolutePath()); + return; + } + + configNodeInfoReadWriteLock.writeLock().lock(); + dataNodeInfoReadWriteLock.writeLock().lock(); + + try (FileInputStream fileInputStream = new FileInputStream(snapshotFile); + DataInputStream dataInputStream = new DataInputStream(fileInputStream); + TIOStreamTransport tioStreamTransport = new TIOStreamTransport(dataInputStream)) { + TProtocol protocol = new TBinaryProtocol(tioStreamTransport); + + clear(); + + nextDataNodeId.set(dataInputStream.readInt()); + + deserializeOnlineDataNode(dataInputStream, protocol); + + deserializeDrainingDataNodes(dataInputStream, protocol); + + } finally { + configNodeInfoReadWriteLock.writeLock().unlock(); + dataNodeInfoReadWriteLock.writeLock().unlock(); + } + } + + private void deserializeOnlineDataNode(DataInputStream inputStream, TProtocol protocol) + throws IOException, TException { + int size = inputStream.readInt(); + while (size > 0) { + int dataNodeId = inputStream.readInt(); + TDataNodeLocation tDataNodeLocation = new TDataNodeLocation(); + tDataNodeLocation.read(protocol); + onlineDataNodes.put(dataNodeId, tDataNodeLocation); + size--; + } + } + + private void deserializeDrainingDataNodes(DataInputStream inputStream, TProtocol protocol) + throws IOException, TException { + int size = inputStream.readInt(); + while (size > 0) { + TDataNodeLocation tDataNodeLocation = new TDataNodeLocation(); + tDataNodeLocation.read(protocol); + drainingDataNodes.add(tDataNodeLocation); + size--; + } + } + + // as drainingDataNodes is not currently implemented, manually set it to validate the test + @TestOnly + public void setDrainingDataNodes(Set tDataNodeLocations) { + drainingDataNodes.addAll(tDataNodeLocations); + } + + public int getNextDataNodeId() { + return nextDataNodeId.get(); } @TestOnly + public Set getDrainingDataNodes() { + return drainingDataNodes; + } + public void clear() { nextDataNodeId = new AtomicInteger(0); onlineDataNodes.clear(); diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigRequestExecutor.java b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigRequestExecutor.java index f69efec57096..fadcd07d6585 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigRequestExecutor.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigRequestExecutor.java @@ -227,6 +227,7 @@ private List getAllAttributes() { List allAttributes = new ArrayList<>(); allAttributes.add(clusterSchemaInfo); allAttributes.add(partitionInfo); + allAttributes.add(nodeInfo); return allAttributes; } } diff --git a/confignode/src/test/java/org/apache/iotdb/confignode/persistence/NodeInfoTest.java b/confignode/src/test/java/org/apache/iotdb/confignode/persistence/NodeInfoTest.java new file mode 100644 index 000000000000..2caf683a1b35 --- /dev/null +++ b/confignode/src/test/java/org/apache/iotdb/confignode/persistence/NodeInfoTest.java @@ -0,0 +1,102 @@ +/* + * 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.iotdb.confignode.persistence; + +import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation; +import org.apache.iotdb.common.rpc.thrift.TEndPoint; +import org.apache.iotdb.confignode.consensus.request.write.RegisterDataNodeReq; + +import org.apache.commons.io.FileUtils; +import org.apache.thrift.TException; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static org.apache.iotdb.db.constant.TestConstant.BASE_OUTPUT_PATH; + +public class NodeInfoTest { + + private static NodeInfo nodeInfo; + private static final File snapshotDir = new File(BASE_OUTPUT_PATH, "snapshot"); + + @BeforeClass + public static void setup() { + nodeInfo = NodeInfo.getInstance(); + if (!snapshotDir.exists()) { + snapshotDir.mkdirs(); + } + } + + @AfterClass + public static void cleanup() throws IOException { + nodeInfo.clear(); + if (snapshotDir.exists()) { + FileUtils.deleteDirectory(snapshotDir); + } + } + + @Test + public void testSnapshot() throws TException, IOException { + + RegisterDataNodeReq registerDataNodeReq = new RegisterDataNodeReq(generateTDataNodeLocation(1)); + nodeInfo.registerDataNode(registerDataNodeReq); + + registerDataNodeReq = new RegisterDataNodeReq(generateTDataNodeLocation(2)); + nodeInfo.registerDataNode(registerDataNodeReq); + + Set drainingDataNodes_before = new HashSet<>(); + // parameter i is used to be flag in generateTDataNodeLocation + for (int i = 3; i < 8; i++) { + drainingDataNodes_before.add(generateTDataNodeLocation(i)); + } + nodeInfo.setDrainingDataNodes(drainingDataNodes_before); + + int nextId = nodeInfo.getNextDataNodeId(); + List onlineDataNodes_before = nodeInfo.getOnlineDataNodes(); + + nodeInfo.processTakeSnapshot(snapshotDir); + nodeInfo.clear(); + nodeInfo.processLoadSnapshot(snapshotDir); + + Assert.assertEquals(nextId, nodeInfo.getNextDataNodeId()); + + Set drainingDataNodes_after = nodeInfo.getDrainingDataNodes(); + Assert.assertEquals(drainingDataNodes_before, drainingDataNodes_after); + + List onlineDataNodes_after = nodeInfo.getOnlineDataNodes(); + + Assert.assertEquals(onlineDataNodes_before, onlineDataNodes_after); + } + + private TDataNodeLocation generateTDataNodeLocation(int flag) { + return new TDataNodeLocation( + 10000 + flag, + new TEndPoint("127.0.0.1", 6600 + flag), + new TEndPoint("127.0.0.1", 7700 + flag), + new TEndPoint("127.0.0.1", 8800 + flag), + new TEndPoint("127.0.0.1", 9900 + flag)); + } +} From 984dfd90585cf31bf024d4042885e0753263caf3 Mon Sep 17 00:00:00 2001 From: ZhangHongYin <46039728+SpriCoder@users.noreply.github.com> Date: Sat, 14 May 2022 00:06:31 +0800 Subject: [PATCH 005/436] [IOTDB-3117][snapshot] add authInfo snapshot. (#5843) --- .../confignode/persistence/AuthorInfo.java | 35 ++- .../persistence/ClusterSchemaInfo.java | 1 + .../confignode/persistence/PartitionInfo.java | 1 + .../executor/ConfigRequestExecutor.java | 2 +- .../persistence/AuthorInfoTest.java | 47 ++++ ...LoadExternalTsFileWithTimePartitionIT.java | 2 +- .../auth/authorizer/BasicAuthorizer.java | 16 ++ .../commons/auth/authorizer/IAuthorizer.java | 4 +- .../commons/auth/role/BasicRoleManager.java | 6 +- .../commons/auth/role/IRoleAccessor.java | 3 +- .../iotdb/commons/auth/role/IRoleManager.java | 3 +- .../auth/role/LocalFileRoleAccessor.java | 45 ++++ .../auth/role/LocalFileRoleManager.java | 15 ++ .../commons/auth/user/BasicUserManager.java | 6 +- .../commons/auth/user/IUserAccessor.java | 3 +- .../iotdb/commons/auth/user/IUserManager.java | 3 +- .../auth/user/LocalFileUserAccessor.java | 48 ++++- .../auth/user/LocalFileUserManager.java | 15 ++ .../commons/snapshot}/SnapshotProcessor.java | 2 +- .../iotdb/commons}/utils/FileUtils.java | 56 ++++- .../iotdb/db/auth/AuthorizerManager.java | 201 +++++++++++++++--- .../db/engine/storagegroup/DataRegion.java | 4 +- .../db/service/metrics/MetricsService.java | 2 +- .../pipedata/queue/BufferedPipeDataQueue.java | 2 +- .../sender/recovery/TsFilePipeLogger.java | 2 +- .../db/wal/recover/WALNodeRecoverTask.java | 2 +- .../engine/compaction/TsFileIdentifierUT.java | 2 +- 27 files changed, 477 insertions(+), 51 deletions(-) rename {confignode/src/main/java/org/apache/iotdb/confignode/persistence => node-commons/src/main/java/org/apache/iotdb/commons/snapshot}/SnapshotProcessor.java (97%) rename {server/src/main/java/org/apache/iotdb/db => node-commons/src/main/java/org/apache/iotdb/commons}/utils/FileUtils.java (54%) diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/AuthorInfo.java b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/AuthorInfo.java index a27c81fd4377..72704fd7da50 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/AuthorInfo.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/AuthorInfo.java @@ -26,26 +26,35 @@ import org.apache.iotdb.commons.auth.entity.PrivilegeType; import org.apache.iotdb.commons.auth.entity.Role; import org.apache.iotdb.commons.auth.entity.User; +import org.apache.iotdb.commons.conf.CommonConfig; +import org.apache.iotdb.commons.conf.CommonDescriptor; import org.apache.iotdb.commons.conf.IoTDBConstant; +import org.apache.iotdb.commons.snapshot.SnapshotProcessor; import org.apache.iotdb.commons.utils.AuthUtils; +import org.apache.iotdb.commons.utils.FileUtils; +import org.apache.iotdb.commons.utils.TestOnly; import org.apache.iotdb.confignode.consensus.request.ConfigRequestType; import org.apache.iotdb.confignode.consensus.request.auth.AuthorReq; import org.apache.iotdb.confignode.consensus.response.PermissionInfoResp; import org.apache.iotdb.rpc.RpcUtils; import org.apache.iotdb.rpc.TSStatusCode; +import org.apache.thrift.TException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.File; +import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; -public class AuthorInfo { +public class AuthorInfo implements SnapshotProcessor { private static final Logger logger = LoggerFactory.getLogger(AuthorInfo.class); + private static final CommonConfig commonConfig = CommonDescriptor.getInstance().getConfig(); private IAuthorizer authorizer; @@ -332,7 +341,6 @@ public PermissionInfoResp executeListUserPrivileges(AuthorReq plan) throws AuthE } private static class AuthorInfoHolder { - private static final AuthorInfo INSTANCE = new AuthorInfo(); private AuthorInfoHolder() { @@ -343,4 +351,27 @@ private AuthorInfoHolder() { public static AuthorInfo getInstance() { return AuthorInfo.AuthorInfoHolder.INSTANCE; } + + @Override + public boolean processTakeSnapshot(File snapshotDir) throws TException, IOException { + return authorizer.processTakeSnapshot(snapshotDir); + } + + @Override + public void processLoadSnapshot(File snapshotDir) throws TException, IOException { + authorizer.processLoadSnapshot(snapshotDir); + } + + @TestOnly + public void clear() throws AuthException { + File userFolder = new File(commonConfig.getUserFolder()); + if (userFolder.exists()) { + FileUtils.deleteDirectory(userFolder); + } + File roleFolder = new File(commonConfig.getRoleFolder()); + if (roleFolder.exists()) { + FileUtils.deleteDirectory(roleFolder); + } + authorizer.reset(); + } } diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/ClusterSchemaInfo.java b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/ClusterSchemaInfo.java index 42e8976494b5..04a5ef47a95a 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/ClusterSchemaInfo.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/ClusterSchemaInfo.java @@ -25,6 +25,7 @@ import org.apache.iotdb.commons.exception.IllegalPathException; import org.apache.iotdb.commons.exception.MetadataException; import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.commons.snapshot.SnapshotProcessor; import org.apache.iotdb.commons.utils.TestOnly; import org.apache.iotdb.confignode.consensus.request.read.CountStorageGroupReq; import org.apache.iotdb.confignode.consensus.request.read.GetStorageGroupReq; diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/PartitionInfo.java b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/PartitionInfo.java index 8a10c7e84f9d..1e36ebb8326b 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/PartitionInfo.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/PartitionInfo.java @@ -26,6 +26,7 @@ import org.apache.iotdb.common.rpc.thrift.TTimePartitionSlot; import org.apache.iotdb.commons.partition.DataPartition; import org.apache.iotdb.commons.partition.SchemaPartition; +import org.apache.iotdb.commons.snapshot.SnapshotProcessor; import org.apache.iotdb.commons.utils.TestOnly; import org.apache.iotdb.confignode.conf.ConfigNodeDescriptor; import org.apache.iotdb.confignode.consensus.request.read.GetDataPartitionReq; diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigRequestExecutor.java b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigRequestExecutor.java index fadcd07d6585..2deed2156902 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigRequestExecutor.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigRequestExecutor.java @@ -20,6 +20,7 @@ import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.commons.auth.AuthException; +import org.apache.iotdb.commons.snapshot.SnapshotProcessor; import org.apache.iotdb.confignode.consensus.request.ConfigRequest; import org.apache.iotdb.confignode.consensus.request.auth.AuthorReq; import org.apache.iotdb.confignode.consensus.request.read.CountStorageGroupReq; @@ -47,7 +48,6 @@ import org.apache.iotdb.confignode.persistence.NodeInfo; import org.apache.iotdb.confignode.persistence.PartitionInfo; import org.apache.iotdb.confignode.persistence.ProcedureInfo; -import org.apache.iotdb.confignode.persistence.SnapshotProcessor; import org.apache.iotdb.consensus.common.DataSet; import org.apache.iotdb.rpc.TSStatusCode; diff --git a/confignode/src/test/java/org/apache/iotdb/confignode/persistence/AuthorInfoTest.java b/confignode/src/test/java/org/apache/iotdb/confignode/persistence/AuthorInfoTest.java index eae73ac4d799..d1f80ff43a4d 100644 --- a/confignode/src/test/java/org/apache/iotdb/confignode/persistence/AuthorInfoTest.java +++ b/confignode/src/test/java/org/apache/iotdb/confignode/persistence/AuthorInfoTest.java @@ -22,31 +22,55 @@ import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.commons.auth.AuthException; import org.apache.iotdb.commons.auth.entity.PrivilegeType; +import org.apache.iotdb.commons.conf.CommonConfig; +import org.apache.iotdb.commons.conf.CommonDescriptor; import org.apache.iotdb.commons.conf.IoTDBConstant; +import org.apache.iotdb.confignode.conf.ConfigNodeConf; +import org.apache.iotdb.confignode.conf.ConfigNodeDescriptor; import org.apache.iotdb.confignode.consensus.request.ConfigRequestType; import org.apache.iotdb.confignode.consensus.request.auth.AuthorReq; import org.apache.iotdb.confignode.consensus.response.PermissionInfoResp; import org.apache.iotdb.confignode.rpc.thrift.TCheckUserPrivilegesReq; import org.apache.iotdb.rpc.TSStatusCode; +import org.apache.commons.io.FileUtils; import org.apache.thrift.TException; +import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; +import java.io.File; +import java.io.IOException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import static org.apache.iotdb.db.constant.TestConstant.BASE_OUTPUT_PATH; + public class AuthorInfoTest { private static AuthorInfo authorInfo; + private static final File snapshotDir = new File(BASE_OUTPUT_PATH, "authorInfo-snapshot"); + private static final ConfigNodeConf config = ConfigNodeDescriptor.getInstance().getConf(); + private static final CommonConfig commonConfig = CommonDescriptor.getInstance().getConfig(); @BeforeClass public static void setup() { authorInfo = AuthorInfo.getInstance(); + if (!snapshotDir.exists()) { + snapshotDir.mkdirs(); + } + } + + @AfterClass + public static void cleanup() throws IOException, AuthException { + authorInfo.clear(); + if (snapshotDir.exists()) { + FileUtils.deleteDirectory(snapshotDir); + } } @Test @@ -289,4 +313,27 @@ private void cleanUserAndRole() throws TException, AuthException { Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); } } + + @Test + public void takeSnapshot() throws TException, IOException, AuthException { + cleanUserAndRole(); + // create role + AuthorReq createRoleReq = new AuthorReq(ConfigRequestType.CreateRole); + createRoleReq.setRoleName("testRole"); + TSStatus status = authorInfo.authorNonQuery(createRoleReq); + Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + AuthorReq createUserReq = new AuthorReq(ConfigRequestType.CreateUser); + createUserReq.setUserName("testUser"); + createUserReq.setPassword("testPassword"); + status = authorInfo.authorNonQuery(createUserReq); + Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + + Assert.assertEquals(1, authorInfo.executeListRole().getPermissionInfo().get("role").size()); + Assert.assertEquals(2, authorInfo.executeListUser().getPermissionInfo().get("user").size()); + Assert.assertTrue(authorInfo.processTakeSnapshot(snapshotDir)); + authorInfo.clear(); + authorInfo.processLoadSnapshot(snapshotDir); + Assert.assertEquals(1, authorInfo.executeListRole().getPermissionInfo().get("role").size()); + Assert.assertEquals(2, authorInfo.executeListUser().getPermissionInfo().get("user").size()); + } } diff --git a/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBLoadExternalTsFileWithTimePartitionIT.java b/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBLoadExternalTsFileWithTimePartitionIT.java index e8d85129ffe8..e8390bf9cdea 100644 --- a/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBLoadExternalTsFileWithTimePartitionIT.java +++ b/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBLoadExternalTsFileWithTimePartitionIT.java @@ -20,11 +20,11 @@ import org.apache.iotdb.commons.exception.IllegalPathException; import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.commons.utils.FileUtils; import org.apache.iotdb.db.conf.IoTDBConfig; import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.engine.StorageEngine; import org.apache.iotdb.db.utils.EnvironmentUtils; -import org.apache.iotdb.db.utils.FileUtils; import org.apache.iotdb.itbase.category.LocalStandaloneTest; import org.apache.iotdb.jdbc.Config; import org.apache.iotdb.tsfile.exception.write.WriteProcessException; diff --git a/node-commons/src/main/java/org/apache/iotdb/commons/auth/authorizer/BasicAuthorizer.java b/node-commons/src/main/java/org/apache/iotdb/commons/auth/authorizer/BasicAuthorizer.java index 81e04716d4f7..ad67bde66f0b 100644 --- a/node-commons/src/main/java/org/apache/iotdb/commons/auth/authorizer/BasicAuthorizer.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/auth/authorizer/BasicAuthorizer.java @@ -30,9 +30,12 @@ import org.apache.iotdb.commons.service.ServiceType; import org.apache.iotdb.commons.utils.AuthUtils; +import org.apache.thrift.TException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.File; +import java.io.IOException; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -164,6 +167,7 @@ public void revokePrivilegeFromUser(String username, String path, int privilegeI @Override public void createRole(String roleName) throws AuthException { if (!roleManager.createRole(roleName)) { + logger.error("Role {} already exists", roleName); throw new AuthException(String.format("Role %s already exists", roleName)); } } @@ -403,4 +407,16 @@ public void replaceAllUsers(Map users) throws AuthException { public void replaceAllRoles(Map roles) throws AuthException { roleManager.replaceAllRoles(roles); } + + @Override + public boolean processTakeSnapshot(File snapshotDir) throws TException, IOException { + return userManager.processTakeSnapshot(snapshotDir) + & roleManager.processTakeSnapshot(snapshotDir); + } + + @Override + public void processLoadSnapshot(File snapshotDir) throws TException, IOException { + userManager.processLoadSnapshot(snapshotDir); + roleManager.processLoadSnapshot(snapshotDir); + } } diff --git a/node-commons/src/main/java/org/apache/iotdb/commons/auth/authorizer/IAuthorizer.java b/node-commons/src/main/java/org/apache/iotdb/commons/auth/authorizer/IAuthorizer.java index 95d897560dd4..d06df9b2cf50 100644 --- a/node-commons/src/main/java/org/apache/iotdb/commons/auth/authorizer/IAuthorizer.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/auth/authorizer/IAuthorizer.java @@ -16,18 +16,20 @@ * specific language governing permissions and limitations * under the License. */ + package org.apache.iotdb.commons.auth.authorizer; import org.apache.iotdb.commons.auth.AuthException; import org.apache.iotdb.commons.auth.entity.Role; import org.apache.iotdb.commons.auth.entity.User; +import org.apache.iotdb.commons.snapshot.SnapshotProcessor; import java.util.List; import java.util.Map; import java.util.Set; /** This interface provides all authorization-relative operations. */ -public interface IAuthorizer { +public interface IAuthorizer extends SnapshotProcessor { /** * Login for a user. diff --git a/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/BasicRoleManager.java b/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/BasicRoleManager.java index facf03d35d56..9dcc646a98b5 100644 --- a/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/BasicRoleManager.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/BasicRoleManager.java @@ -37,9 +37,9 @@ */ public abstract class BasicRoleManager implements IRoleManager { - private Map roleMap; - private IRoleAccessor accessor; - private HashLock lock; + protected Map roleMap; + protected IRoleAccessor accessor; + protected HashLock lock; BasicRoleManager(LocalFileRoleAccessor accessor) { this.roleMap = new HashMap<>(); diff --git a/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/IRoleAccessor.java b/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/IRoleAccessor.java index e6a1f071a0bf..fec9dfa28aa3 100644 --- a/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/IRoleAccessor.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/IRoleAccessor.java @@ -19,12 +19,13 @@ package org.apache.iotdb.commons.auth.role; import org.apache.iotdb.commons.auth.entity.Role; +import org.apache.iotdb.commons.snapshot.SnapshotProcessor; import java.io.IOException; import java.util.List; /** This interface manages the serialization/deserialization of the role objects. */ -public interface IRoleAccessor { +public interface IRoleAccessor extends SnapshotProcessor { /** * Deserialize a role from lower storage. diff --git a/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/IRoleManager.java b/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/IRoleManager.java index 7128e0b82d9e..ccadbd5917f1 100644 --- a/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/IRoleManager.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/IRoleManager.java @@ -20,12 +20,13 @@ import org.apache.iotdb.commons.auth.AuthException; import org.apache.iotdb.commons.auth.entity.Role; +import org.apache.iotdb.commons.snapshot.SnapshotProcessor; import java.util.List; import java.util.Map; /** This interface maintains roles in memory and is responsible for their modifications. */ -public interface IRoleManager { +public interface IRoleManager extends SnapshotProcessor { /** * Get a role object. diff --git a/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/LocalFileRoleAccessor.java b/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/LocalFileRoleAccessor.java index e39ea0cc03c5..f75758b48103 100644 --- a/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/LocalFileRoleAccessor.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/LocalFileRoleAccessor.java @@ -22,8 +22,10 @@ import org.apache.iotdb.commons.auth.entity.Role; import org.apache.iotdb.commons.conf.IoTDBConstant; import org.apache.iotdb.commons.file.SystemFileFactory; +import org.apache.iotdb.commons.utils.FileUtils; import org.apache.iotdb.commons.utils.IOUtils; +import org.apache.thrift.TException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,6 +41,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.UUID; /** * This class store each role in a separate sequential file. Role file schema : Int32 role name size @@ -53,6 +56,7 @@ public class LocalFileRoleAccessor implements IRoleAccessor { private static final Logger logger = LoggerFactory.getLogger(LocalFileRoleAccessor.class); private static final String TEMP_SUFFIX = ".temp"; private static final String STRING_ENCODING = "utf-8"; + private static final String roleSnapshotFileName = "system" + File.separator + "roles"; private String roleDirPath; @@ -183,6 +187,47 @@ public List listAllRoles() { return retList; } + @Override + public boolean processTakeSnapshot(File snapshotDir) throws TException, IOException { + SystemFileFactory systemFileFactory = SystemFileFactory.INSTANCE; + File roleFolder = systemFileFactory.getFile(roleDirPath); + File roleSnapshotDir = systemFileFactory.getFile(snapshotDir, roleSnapshotFileName); + File roleTmpSnapshotDir = + systemFileFactory.getFile(roleSnapshotDir.getAbsolutePath() + "-" + UUID.randomUUID()); + + boolean result = true; + try { + result = FileUtils.copyDir(roleFolder, roleTmpSnapshotDir); + result &= roleTmpSnapshotDir.renameTo(roleSnapshotDir); + } finally { + if (roleTmpSnapshotDir.exists() && !roleTmpSnapshotDir.delete()) { + FileUtils.deleteDirectory(roleTmpSnapshotDir); + } + } + return result; + } + + @Override + public void processLoadSnapshot(File snapshotDir) throws TException, IOException { + SystemFileFactory systemFileFactory = SystemFileFactory.INSTANCE; + File roleFolder = systemFileFactory.getFile(roleDirPath); + File roleTmpFolder = + systemFileFactory.getFile(roleFolder.getAbsolutePath() + "-" + UUID.randomUUID()); + File roleSnapshotDir = systemFileFactory.getFile(snapshotDir, roleSnapshotFileName); + + try { + org.apache.commons.io.FileUtils.moveDirectory(roleFolder, roleTmpFolder); + if (!FileUtils.copyDir(roleSnapshotDir, roleFolder)) { + logger.error("Failed to load role folder snapshot and rollback."); + // rollback if failed to copy + FileUtils.deleteDirectory(roleFolder); + org.apache.commons.io.FileUtils.moveDirectory(roleTmpFolder, roleFolder); + } + } finally { + FileUtils.deleteDirectory(roleTmpFolder); + } + } + @Override public void reset() { if (SystemFileFactory.INSTANCE.getFile(roleDirPath).mkdirs()) { diff --git a/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/LocalFileRoleManager.java b/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/LocalFileRoleManager.java index ecf52a68d3ef..3569e7c90d3a 100644 --- a/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/LocalFileRoleManager.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/LocalFileRoleManager.java @@ -19,9 +19,24 @@ package org.apache.iotdb.commons.auth.role; +import org.apache.thrift.TException; + +import java.io.File; +import java.io.IOException; + public class LocalFileRoleManager extends BasicRoleManager { public LocalFileRoleManager(String roleDirPath) { super(new LocalFileRoleAccessor(roleDirPath)); } + + @Override + public boolean processTakeSnapshot(File snapshotDir) throws TException, IOException { + return accessor.processTakeSnapshot(snapshotDir); + } + + @Override + public void processLoadSnapshot(File snapshotDir) throws TException, IOException { + accessor.processLoadSnapshot(snapshotDir); + } } diff --git a/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/BasicUserManager.java b/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/BasicUserManager.java index e4e04aa62d58..00501f256303 100644 --- a/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/BasicUserManager.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/BasicUserManager.java @@ -45,9 +45,9 @@ public abstract class BasicUserManager implements IUserManager { private static final Logger logger = LoggerFactory.getLogger(BasicUserManager.class); private static final String NO_SUCH_USER_ERROR = "No such user %s"; - private Map userMap; - private IUserAccessor accessor; - private HashLock lock; + protected Map userMap; + protected IUserAccessor accessor; + protected HashLock lock; /** * BasicUserManager Constructor. diff --git a/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/IUserAccessor.java b/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/IUserAccessor.java index cb0cffee738e..0dba5a04aab1 100644 --- a/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/IUserAccessor.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/IUserAccessor.java @@ -19,12 +19,13 @@ package org.apache.iotdb.commons.auth.user; import org.apache.iotdb.commons.auth.entity.User; +import org.apache.iotdb.commons.snapshot.SnapshotProcessor; import java.io.IOException; import java.util.List; /** This interface manages the serialization/deserialization of the user objects. */ -public interface IUserAccessor { +public interface IUserAccessor extends SnapshotProcessor { /** * Deserialize a user from lower storage. diff --git a/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/IUserManager.java b/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/IUserManager.java index 8f538512e165..6abc22e9d846 100644 --- a/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/IUserManager.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/IUserManager.java @@ -20,12 +20,13 @@ import org.apache.iotdb.commons.auth.AuthException; import org.apache.iotdb.commons.auth.entity.User; +import org.apache.iotdb.commons.snapshot.SnapshotProcessor; import java.util.List; import java.util.Map; /** This interface provides accesses to users. */ -public interface IUserManager { +public interface IUserManager extends SnapshotProcessor { /** * Get a user object. diff --git a/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/LocalFileUserAccessor.java b/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/LocalFileUserAccessor.java index 01f08d442853..7ded8041459a 100644 --- a/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/LocalFileUserAccessor.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/LocalFileUserAccessor.java @@ -22,8 +22,10 @@ import org.apache.iotdb.commons.auth.entity.User; import org.apache.iotdb.commons.conf.IoTDBConstant; import org.apache.iotdb.commons.file.SystemFileFactory; +import org.apache.iotdb.commons.utils.FileUtils; import org.apache.iotdb.commons.utils.IOUtils; +import org.apache.thrift.TException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -41,6 +43,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.UUID; /** * This class loads a user's information from the corresponding file.The user file is a sequential @@ -55,10 +58,10 @@ * bytes */ public class LocalFileUserAccessor implements IUserAccessor { - + private static final Logger logger = LoggerFactory.getLogger(LocalFileUserAccessor.class); private static final String TEMP_SUFFIX = ".temp"; private static final String STRING_ENCODING = "utf-8"; - private static final Logger logger = LoggerFactory.getLogger(LocalFileUserAccessor.class); + private static final String userSnapshotFileName = "system" + File.separator + "users"; private String userDirPath; /** @@ -235,6 +238,47 @@ public List listAllUsers() { return retList; } + @Override + public boolean processTakeSnapshot(File snapshotDir) throws TException, IOException { + SystemFileFactory systemFileFactory = SystemFileFactory.INSTANCE; + File userFolder = systemFileFactory.getFile(userDirPath); + File userSnapshotDir = systemFileFactory.getFile(snapshotDir, userSnapshotFileName); + File userTmpSnapshotDir = + systemFileFactory.getFile(userSnapshotDir.getAbsolutePath() + "-" + UUID.randomUUID()); + + boolean result = true; + try { + result = FileUtils.copyDir(userFolder, userTmpSnapshotDir); + result &= userTmpSnapshotDir.renameTo(userSnapshotDir); + } finally { + if (userTmpSnapshotDir.exists() && !userTmpSnapshotDir.delete()) { + FileUtils.deleteDirectory(userTmpSnapshotDir); + } + } + return result; + } + + @Override + public void processLoadSnapshot(File snapshotDir) throws TException, IOException { + SystemFileFactory systemFileFactory = SystemFileFactory.INSTANCE; + File userFolder = systemFileFactory.getFile(userDirPath); + File userTmpFolder = + systemFileFactory.getFile(userFolder.getAbsolutePath() + "-" + UUID.randomUUID()); + File userSnapshotDir = systemFileFactory.getFile(snapshotDir, userSnapshotFileName); + + try { + org.apache.commons.io.FileUtils.moveDirectory(userFolder, userTmpFolder); + if (!FileUtils.copyDir(userSnapshotDir, userFolder)) { + logger.error("Failed to load user folder snapshot and rollback."); + // rollback if failed to copy + FileUtils.deleteDirectory(userFolder); + org.apache.commons.io.FileUtils.moveDirectory(userTmpFolder, userFolder); + } + } finally { + FileUtils.deleteDirectory(userTmpFolder); + } + } + @Override public void reset() { if (SystemFileFactory.INSTANCE.getFile(userDirPath).mkdirs()) { diff --git a/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/LocalFileUserManager.java b/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/LocalFileUserManager.java index de38b7d4cae6..b0f0364822c6 100644 --- a/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/LocalFileUserManager.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/LocalFileUserManager.java @@ -20,9 +20,24 @@ import org.apache.iotdb.commons.auth.AuthException; +import org.apache.thrift.TException; + +import java.io.File; +import java.io.IOException; + public class LocalFileUserManager extends BasicUserManager { public LocalFileUserManager(String userDirPath) throws AuthException { super(new LocalFileUserAccessor(userDirPath)); } + + @Override + public boolean processTakeSnapshot(File snapshotDir) throws TException, IOException { + return accessor.processTakeSnapshot(snapshotDir); + } + + @Override + public void processLoadSnapshot(File snapshotDir) throws TException, IOException { + accessor.processLoadSnapshot(snapshotDir); + } } diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/SnapshotProcessor.java b/node-commons/src/main/java/org/apache/iotdb/commons/snapshot/SnapshotProcessor.java similarity index 97% rename from confignode/src/main/java/org/apache/iotdb/confignode/persistence/SnapshotProcessor.java rename to node-commons/src/main/java/org/apache/iotdb/commons/snapshot/SnapshotProcessor.java index 1ff79b5a83e9..06a69f568dff 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/SnapshotProcessor.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/snapshot/SnapshotProcessor.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.confignode.persistence; +package org.apache.iotdb.commons.snapshot; import org.apache.thrift.TException; diff --git a/server/src/main/java/org/apache/iotdb/db/utils/FileUtils.java b/node-commons/src/main/java/org/apache/iotdb/commons/utils/FileUtils.java similarity index 54% rename from server/src/main/java/org/apache/iotdb/db/utils/FileUtils.java rename to node-commons/src/main/java/org/apache/iotdb/commons/utils/FileUtils.java index c062fd401ae4..6296a46e4245 100644 --- a/server/src/main/java/org/apache/iotdb/db/utils/FileUtils.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/utils/FileUtils.java @@ -16,14 +16,19 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.iotdb.db.utils; +package org.apache.iotdb.commons.utils; import org.apache.iotdb.commons.file.SystemFileFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; import java.nio.file.DirectoryNotEmptyException; import java.nio.file.Files; import java.nio.file.NoSuchFileException; @@ -32,6 +37,8 @@ public class FileUtils { private static Logger logger = LoggerFactory.getLogger(FileUtils.class); + private static final int bufferSize = 1024; + private FileUtils() {} public static void deleteDirectory(File folder) { @@ -49,6 +56,53 @@ public static void deleteDirectory(File folder) { } } + public static boolean copyDir(File sourceDir, File targetDir) throws IOException { + if (!sourceDir.exists() || !sourceDir.isDirectory()) { + logger.error( + "Failed to copy folder, because source folder [{}] doesn't exist.", + sourceDir.getAbsolutePath()); + return false; + } + if (!targetDir.exists()) { + if (!targetDir.mkdirs()) { + logger.error( + "Failed to copy folder, because failed to create target folder[{}].", + targetDir.getAbsolutePath()); + return false; + } + } else if (!targetDir.isDirectory()) { + logger.error( + "Failed to copy folder, because target folder [{}] already exist.", + targetDir.getAbsolutePath()); + return false; + } + File[] files = sourceDir.listFiles(); + if (files == null || files.length == 0) { + return true; + } + boolean result = true; + for (File file : files) { + if (!file.exists()) { + continue; + } + File targetFile = new File(targetDir, file.getName()); + if (file.isDirectory()) { + result &= copyDir(file.getAbsoluteFile(), targetFile); + } else { + // copy file + try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(file)); + BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(targetFile))) { + byte[] bytes = new byte[bufferSize]; + int size = 0; + while ((size = in.read(bytes)) > 0) { + out.write(bytes, 0, size); + } + } + } + } + return result; + } + /** * Calculate the directory size including sub dir. * diff --git a/server/src/main/java/org/apache/iotdb/db/auth/AuthorizerManager.java b/server/src/main/java/org/apache/iotdb/db/auth/AuthorizerManager.java index 84c79e42e946..7ea4e3f22520 100644 --- a/server/src/main/java/org/apache/iotdb/db/auth/AuthorizerManager.java +++ b/server/src/main/java/org/apache/iotdb/db/auth/AuthorizerManager.java @@ -25,22 +25,28 @@ import org.apache.iotdb.commons.auth.entity.Role; import org.apache.iotdb.commons.auth.entity.User; +import org.apache.thrift.TException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.File; +import java.io.IOException; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.locks.ReentrantReadWriteLock; public class AuthorizerManager implements IAuthorizer { private static final Logger logger = LoggerFactory.getLogger(AuthorizerManager.class); private IAuthorizer iAuthorizer; + private ReentrantReadWriteLock snapshotLock; public AuthorizerManager() { try { iAuthorizer = BasicAuthorizer.getInstance(); + snapshotLock = new ReentrantReadWriteLock(); } catch (AuthException e) { logger.error(e.getMessage()); } @@ -59,77 +65,147 @@ public static AuthorizerManager getInstance() { @Override public boolean login(String username, String password) throws AuthException { - return iAuthorizer.login(username, password); + snapshotLock.readLock().lock(); + try { + return iAuthorizer.login(username, password); + } finally { + snapshotLock.readLock().unlock(); + } } @Override public void createUser(String username, String password) throws AuthException { - iAuthorizer.createUser(username, password); + snapshotLock.readLock().lock(); + try { + iAuthorizer.createUser(username, password); + } finally { + snapshotLock.readLock().unlock(); + } } @Override public void deleteUser(String username) throws AuthException { - iAuthorizer.deleteUser(username); + snapshotLock.readLock().lock(); + try { + iAuthorizer.deleteUser(username); + } finally { + snapshotLock.readLock().unlock(); + } } @Override public void grantPrivilegeToUser(String username, String path, int privilegeId) throws AuthException { - iAuthorizer.grantPrivilegeToUser(username, path, privilegeId); + snapshotLock.readLock().lock(); + try { + iAuthorizer.grantPrivilegeToUser(username, path, privilegeId); + } finally { + snapshotLock.readLock().unlock(); + } } @Override public void revokePrivilegeFromUser(String username, String path, int privilegeId) throws AuthException { - iAuthorizer.revokePrivilegeFromUser(username, path, privilegeId); + snapshotLock.readLock().lock(); + try { + iAuthorizer.revokePrivilegeFromUser(username, path, privilegeId); + } finally { + snapshotLock.readLock().unlock(); + } } @Override public void createRole(String roleName) throws AuthException { - iAuthorizer.createRole(roleName); + snapshotLock.readLock().lock(); + try { + iAuthorizer.createRole(roleName); + } finally { + snapshotLock.readLock().unlock(); + } } @Override public void deleteRole(String roleName) throws AuthException { - iAuthorizer.deleteRole(roleName); + snapshotLock.readLock().lock(); + try { + iAuthorizer.deleteRole(roleName); + } finally { + snapshotLock.readLock().unlock(); + } } @Override public void grantPrivilegeToRole(String roleName, String path, int privilegeId) throws AuthException { - iAuthorizer.grantPrivilegeToRole(roleName, path, privilegeId); + snapshotLock.readLock().lock(); + try { + iAuthorizer.grantPrivilegeToRole(roleName, path, privilegeId); + } finally { + snapshotLock.readLock().unlock(); + } } @Override public void revokePrivilegeFromRole(String roleName, String path, int privilegeId) throws AuthException { - iAuthorizer.revokePrivilegeFromRole(roleName, path, privilegeId); + snapshotLock.readLock().lock(); + try { + iAuthorizer.revokePrivilegeFromRole(roleName, path, privilegeId); + } finally { + snapshotLock.readLock().unlock(); + } } @Override public void grantRoleToUser(String roleName, String username) throws AuthException { - iAuthorizer.grantRoleToUser(roleName, username); + snapshotLock.readLock().lock(); + try { + iAuthorizer.grantRoleToUser(roleName, username); + } finally { + snapshotLock.readLock().unlock(); + } } @Override public void revokeRoleFromUser(String roleName, String username) throws AuthException { - iAuthorizer.revokeRoleFromUser(roleName, username); + snapshotLock.readLock().lock(); + try { + iAuthorizer.revokeRoleFromUser(roleName, username); + } finally { + snapshotLock.readLock().unlock(); + } } @Override public Set getPrivileges(String username, String path) throws AuthException { - return iAuthorizer.getPrivileges(username, path); + snapshotLock.readLock().lock(); + try { + return iAuthorizer.getPrivileges(username, path); + } finally { + snapshotLock.readLock().unlock(); + } } @Override public void updateUserPassword(String username, String newPassword) throws AuthException { - iAuthorizer.updateUserPassword(username, newPassword); + snapshotLock.readLock().lock(); + try { + iAuthorizer.updateUserPassword(username, newPassword); + } finally { + snapshotLock.readLock().unlock(); + } } @Override public boolean checkUserPrivileges(String username, String path, int privilegeId) throws AuthException { - return iAuthorizer.checkUserPrivileges(username, path, privilegeId); + snapshotLock.readLock().lock(); + try { + return iAuthorizer.checkUserPrivileges(username, path, privilegeId); + } finally { + snapshotLock.readLock().unlock(); + } } @Override @@ -139,56 +215,131 @@ public void reset() throws AuthException { @Override public List listAllUsers() { - return iAuthorizer.listAllUsers(); + snapshotLock.readLock().lock(); + try { + return iAuthorizer.listAllUsers(); + } finally { + snapshotLock.readLock().unlock(); + } } @Override public List listAllRoles() { - return iAuthorizer.listAllRoles(); + snapshotLock.readLock().lock(); + try { + return iAuthorizer.listAllRoles(); + } finally { + snapshotLock.readLock().unlock(); + } } @Override public Role getRole(String roleName) throws AuthException { - return iAuthorizer.getRole(roleName); + snapshotLock.readLock().lock(); + try { + return iAuthorizer.getRole(roleName); + } finally { + snapshotLock.readLock().unlock(); + } } @Override public User getUser(String username) throws AuthException { - return iAuthorizer.getUser(username); + snapshotLock.readLock().lock(); + try { + return iAuthorizer.getUser(username); + } finally { + snapshotLock.readLock().unlock(); + } } @Override public boolean isUserUseWaterMark(String userName) throws AuthException { - return iAuthorizer.isUserUseWaterMark(userName); + snapshotLock.readLock().lock(); + try { + return iAuthorizer.isUserUseWaterMark(userName); + } finally { + snapshotLock.readLock().unlock(); + } } @Override public void setUserUseWaterMark(String userName, boolean useWaterMark) throws AuthException { - iAuthorizer.setUserUseWaterMark(userName, useWaterMark); + snapshotLock.readLock().lock(); + try { + iAuthorizer.setUserUseWaterMark(userName, useWaterMark); + } finally { + snapshotLock.readLock().unlock(); + } } @Override public Map getAllUserWaterMarkStatus() { - return iAuthorizer.getAllUserWaterMarkStatus(); + snapshotLock.readLock().lock(); + try { + return iAuthorizer.getAllUserWaterMarkStatus(); + } finally { + snapshotLock.readLock().unlock(); + } } @Override public Map getAllUsers() { - return iAuthorizer.getAllUsers(); + snapshotLock.readLock().lock(); + try { + return iAuthorizer.getAllUsers(); + } finally { + snapshotLock.readLock().unlock(); + } } @Override public Map getAllRoles() { - return iAuthorizer.getAllRoles(); + snapshotLock.readLock().lock(); + try { + return iAuthorizer.getAllRoles(); + } finally { + snapshotLock.readLock().unlock(); + } } @Override public void replaceAllUsers(Map users) throws AuthException { - iAuthorizer.replaceAllUsers(users); + snapshotLock.readLock().lock(); + try { + iAuthorizer.replaceAllUsers(users); + } finally { + snapshotLock.readLock().unlock(); + } } @Override public void replaceAllRoles(Map roles) throws AuthException { - iAuthorizer.replaceAllRoles(roles); + snapshotLock.readLock().lock(); + try { + iAuthorizer.replaceAllRoles(roles); + } finally { + snapshotLock.readLock().unlock(); + } + } + + @Override + public boolean processTakeSnapshot(File snapshotDir) throws TException, IOException { + snapshotLock.writeLock().lock(); + try { + return iAuthorizer.processTakeSnapshot(snapshotDir); + } finally { + snapshotLock.writeLock().unlock(); + } + } + + @Override + public void processLoadSnapshot(File snapshotDir) throws TException, IOException { + snapshotLock.writeLock().lock(); + try { + iAuthorizer.processLoadSnapshot(snapshotDir); + } finally { + snapshotLock.writeLock().unlock(); + } } } diff --git a/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/DataRegion.java b/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/DataRegion.java index 7112596005be..1fbd2ab5e0a4 100755 --- a/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/DataRegion.java +++ b/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/DataRegion.java @@ -1571,7 +1571,7 @@ public void deleteFolder(String systemDir) { try { File storageGroupFolder = SystemFileFactory.INSTANCE.getFile(systemDir, dataRegionId); if (storageGroupFolder.exists()) { - org.apache.iotdb.db.utils.FileUtils.deleteDirectory(storageGroupFolder); + org.apache.iotdb.commons.utils.FileUtils.deleteDirectory(storageGroupFolder); } } finally { writeUnlock(); @@ -1628,7 +1628,7 @@ private void deleteAllSGFolders(List folder) { File storageGroupFolder = fsFactory.getFile(tsfilePath, logicalStorageGroupName + File.separator + dataRegionId); if (storageGroupFolder.exists()) { - org.apache.iotdb.db.utils.FileUtils.deleteDirectory(storageGroupFolder); + org.apache.iotdb.commons.utils.FileUtils.deleteDirectory(storageGroupFolder); } } } diff --git a/server/src/main/java/org/apache/iotdb/db/service/metrics/MetricsService.java b/server/src/main/java/org/apache/iotdb/db/service/metrics/MetricsService.java index 294bd508c059..88d0cc2208ad 100644 --- a/server/src/main/java/org/apache/iotdb/db/service/metrics/MetricsService.java +++ b/server/src/main/java/org/apache/iotdb/db/service/metrics/MetricsService.java @@ -24,8 +24,8 @@ import org.apache.iotdb.commons.service.IService; import org.apache.iotdb.commons.service.JMXService; import org.apache.iotdb.commons.service.ServiceType; +import org.apache.iotdb.commons.utils.FileUtils; import org.apache.iotdb.db.conf.IoTDBDescriptor; -import org.apache.iotdb.db.utils.FileUtils; import org.apache.iotdb.db.wal.node.WALNode; import org.apache.iotdb.metrics.MetricService; import org.apache.iotdb.metrics.config.ReloadLevel; diff --git a/server/src/main/java/org/apache/iotdb/db/sync/pipedata/queue/BufferedPipeDataQueue.java b/server/src/main/java/org/apache/iotdb/db/sync/pipedata/queue/BufferedPipeDataQueue.java index 7c8d51f53181..7df0161272d8 100644 --- a/server/src/main/java/org/apache/iotdb/db/sync/pipedata/queue/BufferedPipeDataQueue.java +++ b/server/src/main/java/org/apache/iotdb/db/sync/pipedata/queue/BufferedPipeDataQueue.java @@ -19,11 +19,11 @@ package org.apache.iotdb.db.sync.pipedata.queue; import org.apache.iotdb.commons.exception.IllegalPathException; +import org.apache.iotdb.commons.utils.FileUtils; import org.apache.iotdb.db.sync.conf.SyncConstant; import org.apache.iotdb.db.sync.conf.SyncPathUtil; import org.apache.iotdb.db.sync.pipedata.PipeData; import org.apache.iotdb.db.sync.pipedata.TsFilePipeData; -import org.apache.iotdb.db.utils.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/server/src/main/java/org/apache/iotdb/db/sync/sender/recovery/TsFilePipeLogger.java b/server/src/main/java/org/apache/iotdb/db/sync/sender/recovery/TsFilePipeLogger.java index 6aa4e3309c0c..02bd032471f0 100644 --- a/server/src/main/java/org/apache/iotdb/db/sync/sender/recovery/TsFilePipeLogger.java +++ b/server/src/main/java/org/apache/iotdb/db/sync/sender/recovery/TsFilePipeLogger.java @@ -20,12 +20,12 @@ package org.apache.iotdb.db.sync.sender.recovery; import org.apache.iotdb.commons.conf.IoTDBConstant; +import org.apache.iotdb.commons.utils.FileUtils; import org.apache.iotdb.db.engine.modification.ModificationFile; import org.apache.iotdb.db.engine.storagegroup.TsFileResource; import org.apache.iotdb.db.sync.conf.SyncConstant; import org.apache.iotdb.db.sync.conf.SyncPathUtil; import org.apache.iotdb.db.sync.sender.pipe.TsFilePipe; -import org.apache.iotdb.db.utils.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/server/src/main/java/org/apache/iotdb/db/wal/recover/WALNodeRecoverTask.java b/server/src/main/java/org/apache/iotdb/db/wal/recover/WALNodeRecoverTask.java index f8f533529c30..477e93a4087d 100644 --- a/server/src/main/java/org/apache/iotdb/db/wal/recover/WALNodeRecoverTask.java +++ b/server/src/main/java/org/apache/iotdb/db/wal/recover/WALNodeRecoverTask.java @@ -18,7 +18,7 @@ */ package org.apache.iotdb.db.wal.recover; -import org.apache.iotdb.db.utils.FileUtils; +import org.apache.iotdb.commons.utils.FileUtils; import org.apache.iotdb.db.wal.buffer.WALEntry; import org.apache.iotdb.db.wal.checkpoint.MemTableInfo; import org.apache.iotdb.db.wal.io.WALReader; diff --git a/server/src/test/java/org/apache/iotdb/db/engine/compaction/TsFileIdentifierUT.java b/server/src/test/java/org/apache/iotdb/db/engine/compaction/TsFileIdentifierUT.java index 5ca09317fa2e..dab3c747676f 100644 --- a/server/src/test/java/org/apache/iotdb/db/engine/compaction/TsFileIdentifierUT.java +++ b/server/src/test/java/org/apache/iotdb/db/engine/compaction/TsFileIdentifierUT.java @@ -19,10 +19,10 @@ package org.apache.iotdb.db.engine.compaction; +import org.apache.iotdb.commons.utils.FileUtils; import org.apache.iotdb.db.conf.IoTDBConfig; import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.engine.compaction.log.TsFileIdentifier; -import org.apache.iotdb.db.utils.FileUtils; import org.junit.Assert; import org.junit.Test; From 2d43fd913f6210574704d3d2f40e0c1097e21d6e Mon Sep 17 00:00:00 2001 From: Alan Choo <43991780+HeimingZ@users.noreply.github.com> Date: Sat, 14 May 2022 00:10:59 +0800 Subject: [PATCH 006/436] [IOTDB-3045] Deleted timeseries are created again after restart (#5897) --- .../iotdb/db/integration/IoTDBRestartIT.java | 29 ++++++++++++------- .../org/apache/iotdb/db/service/IoTDB.java | 14 +++++++++ 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBRestartIT.java b/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBRestartIT.java index b835a650b1ea..2b940cfe0066 100644 --- a/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBRestartIT.java +++ b/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBRestartIT.java @@ -312,12 +312,17 @@ public void testRecoverWALMismatchDataType() throws Exception { boolean hasResultSet = statement.execute("select * from root.**"); assertTrue(hasResultSet); - ResultSet resultSet = statement.getResultSet(); - int cnt = 0; - while (resultSet.next()) { - cnt++; + try (ResultSet resultSet = statement.getResultSet()) { + int cnt = 0; + Assert.assertEquals(3, resultSet.getMetaData().getColumnCount()); + while (resultSet.next()) { + Assert.assertEquals("1", resultSet.getString(1)); + Assert.assertEquals(null, resultSet.getString(2)); + Assert.assertEquals("2.2", resultSet.getString(3)); + cnt++; + } + assertEquals(1, cnt); } - assertEquals(1, cnt); } EnvironmentUtils.cleanEnv(); @@ -345,12 +350,16 @@ public void testRecoverWALDeleteSchema() throws Exception { boolean hasResultSet = statement.execute("select * from root.**"); assertTrue(hasResultSet); - ResultSet resultSet = statement.getResultSet(); - int cnt = 0; - while (resultSet.next()) { - cnt++; + try (ResultSet resultSet = statement.getResultSet()) { + int cnt = 0; + Assert.assertEquals(2, resultSet.getMetaData().getColumnCount()); + while (resultSet.next()) { + Assert.assertEquals("1", resultSet.getString(1)); + Assert.assertEquals("2.2", resultSet.getString(2)); + cnt++; + } + assertEquals(1, cnt); } - assertEquals(1, cnt); } EnvironmentUtils.cleanEnv(); diff --git a/server/src/main/java/org/apache/iotdb/db/service/IoTDB.java b/server/src/main/java/org/apache/iotdb/db/service/IoTDB.java index ba315a5ea12b..54e2b3073a84 100644 --- a/server/src/main/java/org/apache/iotdb/db/service/IoTDB.java +++ b/server/src/main/java/org/apache/iotdb/db/service/IoTDB.java @@ -25,6 +25,7 @@ import org.apache.iotdb.commons.service.JMXService; import org.apache.iotdb.commons.service.RegisterManager; import org.apache.iotdb.commons.service.StartupChecks; +import org.apache.iotdb.db.conf.IoTDBConfig; import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.conf.IoTDBStartCheck; import org.apache.iotdb.db.conf.rest.IoTDBRestServiceCheck; @@ -66,6 +67,7 @@ public class IoTDB implements IoTDBMBean { private static final Logger logger = LoggerFactory.getLogger(IoTDB.class); private final String mbeanName = String.format("%s:%s=%s", IoTDBConstant.IOTDB_PACKAGE, IoTDBConstant.JMX_TYPE, "IoTDB"); + private static final IoTDBConfig config = IoTDBDescriptor.getInstance().getConfig(); private static final RegisterManager registerManager = new RegisterManager(); public static LocalSchemaProcessor schemaProcessor = LocalSchemaProcessor.getInstance(); public static LocalConfigNode configManager = LocalConfigNode.getInstance(); @@ -114,6 +116,13 @@ public void active() { "{}: failed to start because some checks failed. ", IoTDBConstant.GLOBAL_DB_NAME, e); return; } + + // set recover config, avoid creating deleted time series when recovering wal + boolean prevIsAutoCreateSchemaEnabled = config.isAutoCreateSchemaEnabled(); + config.setAutoCreateSchemaEnabled(false); + boolean prevIsEnablePartialInsert = config.isEnablePartialInsert(); + config.setEnablePartialInsert(true); + try { setUp(); } catch (StartupException | QueryProcessException e) { @@ -122,6 +131,11 @@ public void active() { logger.error("{} exit", IoTDBConstant.GLOBAL_DB_NAME); return; } + + // reset config + config.setAutoCreateSchemaEnabled(prevIsAutoCreateSchemaEnabled); + config.setEnablePartialInsert(prevIsEnablePartialInsert); + logger.info("{} has started.", IoTDBConstant.GLOBAL_DB_NAME); } From 13c732b43a307fe2ecbf6cd990cc537f8c73eceb Mon Sep 17 00:00:00 2001 From: Mrquan <50790061+MrQuansy@users.noreply.github.com> Date: Sat, 14 May 2022 09:35:11 +0800 Subject: [PATCH 007/436] Scripts of stop and remove datanode (#5894) --- .../resources/sbin/remove-datanode.bat | 109 ++++++++++++++++++ .../resources/sbin/remove-datanode.sh | 72 ++++++++++++ .../assembly/resources/sbin/stop-datanode.sh | 20 +--- 3 files changed, 186 insertions(+), 15 deletions(-) create mode 100644 server/src/assembly/resources/sbin/remove-datanode.bat create mode 100644 server/src/assembly/resources/sbin/remove-datanode.sh diff --git a/server/src/assembly/resources/sbin/remove-datanode.bat b/server/src/assembly/resources/sbin/remove-datanode.bat new file mode 100644 index 000000000000..45e18b375b06 --- /dev/null +++ b/server/src/assembly/resources/sbin/remove-datanode.bat @@ -0,0 +1,109 @@ +@REM +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM + +@echo off +echo ```````````````````````` +echo Starting to remove a DataNode +echo ```````````````````````` + +PATH %PATH%;%JAVA_HOME%\bin\ +set "FULL_VERSION=" +set "MAJOR_VERSION=" +set "MINOR_VERSION=" + + +for /f tokens^=2-5^ delims^=.-_+^" %%j in ('java -fullversion 2^>^&1') do ( + set "FULL_VERSION=%%j-%%k-%%l-%%m" + IF "%%j" == "1" ( + set "MAJOR_VERSION=%%k" + set "MINOR_VERSION=%%l" + ) else ( + set "MAJOR_VERSION=%%j" + set "MINOR_VERSION=%%k" + ) +) + +set JAVA_VERSION=%MAJOR_VERSION% + +IF NOT %JAVA_VERSION% == 8 ( + IF NOT %JAVA_VERSION% == 11 ( + echo IoTDB only supports jdk8 or jdk11, please check your java version. + goto finally + ) +) + +if "%OS%" == "Windows_NT" setlocal + +pushd %~dp0.. +if NOT DEFINED IOTDB_HOME set IOTDB_HOME=%cd% +popd + +set IOTDB_CONF=%IOTDB_HOME%\conf +set IOTDB_LOGS=%IOTDB_HOME%\logs + +@setlocal ENABLEDELAYEDEXPANSION ENABLEEXTENSIONS +set CONF_PARAMS=-r +set is_conf_path=false +for %%i in (%*) do ( + set CONF_PARAMS=!CONF_PARAMS! %%i +) + +if NOT DEFINED MAIN_CLASS set MAIN_CLASS=org.apache.iotdb.db.service.DataNode +if NOT DEFINED JAVA_HOME goto :err + +@REM ----------------------------------------------------------------------------- +@REM JVM Opts we'll use in legacy run or installation +set JAVA_OPTS=-ea^ + -Dlogback.configurationFile="%IOTDB_CONF%\logback.xml"^ + -DIOTDB_HOME="%IOTDB_HOME%"^ + -DTSFILE_HOME="%IOTDB_HOME%"^ + -DIOTDB_CONF="%IOTDB_CONF%" + +@REM ***** CLASSPATH library setting ***** +@REM Ensure that any user defined CLASSPATH variables are not used on startup +set CLASSPATH="%IOTDB_HOME%\lib" + +@REM For each jar in the IOTDB_HOME lib directory call append to build the CLASSPATH variable. +set CLASSPATH=%CLASSPATH%;"%IOTDB_HOME%\lib\*" +set CLASSPATH=%CLASSPATH%;iotdb.db.service.DataNode +goto okClasspath + +:append +set CLASSPATH=%CLASSPATH%;%1 +goto :eof + +@REM ----------------------------------------------------------------------------- +:okClasspath + +rem echo CLASSPATH: %CLASSPATH% + +"%JAVA_HOME%\bin\java" %JAVA_OPTS% %IOTDB_HEAP_OPTS% -cp %CLASSPATH% %IOTDB_JMX_OPTS% %MAIN_CLASS% %CONF_PARAMS% +goto finally + +:err +echo JAVA_HOME environment variable must be set! +pause + + +@REM ----------------------------------------------------------------------------- +:finally + +pause + +ENDLOCAL \ No newline at end of file diff --git a/server/src/assembly/resources/sbin/remove-datanode.sh b/server/src/assembly/resources/sbin/remove-datanode.sh new file mode 100644 index 000000000000..350e012d4874 --- /dev/null +++ b/server/src/assembly/resources/sbin/remove-datanode.sh @@ -0,0 +1,72 @@ +#!/bin/sh +# +# 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. +# + +echo --------------------- +echo "Starting to remove a DataNode" +echo --------------------- + +if [ -z "${IOTDB_HOME}" ]; then + export IOTDB_HOME="`dirname "$0"`/.." +fi + +IOTDB_CONF=${IOTDB_HOME}/conf + +CONF_PARAMS="-r "$* + +if [ -n "$JAVA_HOME" ]; then + for java in "$JAVA_HOME"/bin/amd64/java "$JAVA_HOME"/bin/java; do + if [ -x "$java" ]; then + JAVA="$java" + break + fi + done +else + JAVA=java +fi + +if [ -z $JAVA ] ; then + echo Unable to find java executable. Check JAVA_HOME and PATH environment variables. > /dev/stderr + exit 1; +fi + +CLASSPATH="" +for f in ${IOTDB_HOME}/lib/*.jar; do + CLASSPATH=${CLASSPATH}":"$f +done +classname=org.apache.iotdb.db.service.DataNode + +launch_service() +{ + class="$1" + iotdb_parms="-Dlogback.configurationFile=${IOTDB_CONF}/logback.xml" + iotdb_parms="$iotdb_parms -DIOTDB_HOME=${IOTDB_HOME}" + iotdb_parms="$iotdb_parms -DTSFILE_HOME=${IOTDB_HOME}" + iotdb_parms="$iotdb_parms -DIOTDB_CONF=${IOTDB_CONF}" + iotdb_parms="$iotdb_parms -Dname=iotdb\.IoTDB" + exec "$JAVA" $iotdb_parms $IOTDB_JMX_OPTS -cp "$CLASSPATH" "$class" $CONF_PARAMS + return $? +} + +# Start up the service +launch_service "$classname" + +exit $? + + diff --git a/server/src/assembly/resources/sbin/stop-datanode.sh b/server/src/assembly/resources/sbin/stop-datanode.sh index efbbd4360534..10e19c5aa93e 100644 --- a/server/src/assembly/resources/sbin/stop-datanode.sh +++ b/server/src/assembly/resources/sbin/stop-datanode.sh @@ -20,20 +20,10 @@ PIDS=$(ps ax | grep -i 'DataNode' | grep java | grep -v grep | awk '{print $1}') -sig=0 -for every_pid in ${PIDS} -do - cwd_path=$(ls -l /proc/$every_pid | grep "cwd ->" | grep -v grep | awk '{print $NF}') - pwd_path=$(/bin/pwd) - if [[ $pwd_path =~ $cwd_path ]]; then - kill -s TERM $every_pid - echo "close IoTDB" - sig=1 - fi -done - -if [ $sig -eq 0 ]; then - echo "No IoTDB server to stop" - exit 1 +if [ ! $PIDS ];then + echo "No DataNode to stop" +else + echo "Stop DataNode" + jps | grep -w DataNode | grep -v grep | awk '{print $1}' | xargs kill -9 fi From d93a848fd71abc5e1371b05b55ff032e50dd073c Mon Sep 17 00:00:00 2001 From: Haonan Date: Sat, 14 May 2022 11:57:26 +0800 Subject: [PATCH 008/436] Fix config node compile error (#5905) --- .../java/org/apache/iotdb/confignode/persistence/NodeInfo.java | 1 + 1 file changed, 1 insertion(+) diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/NodeInfo.java b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/NodeInfo.java index 818cf7adedab..8fe90d1c0ca1 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/NodeInfo.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/NodeInfo.java @@ -21,6 +21,7 @@ import org.apache.iotdb.common.rpc.thrift.TConfigNodeLocation; import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation; import org.apache.iotdb.common.rpc.thrift.TSStatus; +import org.apache.iotdb.commons.snapshot.SnapshotProcessor; import org.apache.iotdb.commons.utils.NodeUrlUtils; import org.apache.iotdb.commons.utils.TestOnly; import org.apache.iotdb.confignode.conf.ConfigNodeConstant; From fe253ffdfdb94aec990472f99dc9ae20540ab859 Mon Sep 17 00:00:00 2001 From: ZhangHongYin <46039728+SpriCoder@users.noreply.github.com> Date: Sat, 14 May 2022 15:12:53 +0800 Subject: [PATCH 009/436] [IOTDB-3158][metric] fix NPE exception (#5891) --- .../dropwizard/reporter/IoTDBReporter.java | 15 +++++ .../reporter/IoTDBMeterRegistry.java | 66 ++++++++++--------- 2 files changed, 49 insertions(+), 32 deletions(-) diff --git a/metrics/dropwizard-metrics/src/main/java/org/apache/iotdb/metrics/dropwizard/reporter/IoTDBReporter.java b/metrics/dropwizard-metrics/src/main/java/org/apache/iotdb/metrics/dropwizard/reporter/IoTDBReporter.java index 5446167feac4..b2452d0deadb 100644 --- a/metrics/dropwizard-metrics/src/main/java/org/apache/iotdb/metrics/dropwizard/reporter/IoTDBReporter.java +++ b/metrics/dropwizard-metrics/src/main/java/org/apache/iotdb/metrics/dropwizard/reporter/IoTDBReporter.java @@ -156,6 +156,9 @@ public void report( } private void sendGauge(String name, Gauge gauge) { + if (null == gauge) { + return; + } MetricName metricName = new MetricName(name); Object obj = gauge.getValue(); double value; @@ -168,12 +171,18 @@ private void sendGauge(String name, Gauge gauge) { } private void sendCounter(String name, Counter counter) { + if (null == counter) { + return; + } MetricName metricName = new MetricName(name); double value = counter.getCount(); updateValue(prefixed(metricName.getName()), metricName.getTags(), value); } private void sendHistogram(String name, Histogram histogram) { + if (null == histogram) { + return; + } MetricName metricName = new MetricName(name); writeSnapshotAndCount( prefixed(metricName.getName()), @@ -184,12 +193,18 @@ private void sendHistogram(String name, Histogram histogram) { } private void sendMeter(String name, Meter meter) { + if (null == meter) { + return; + } MetricName metricName = new MetricName(name); double value = meter.getCount(); updateValue(prefixed(metricName.getName()), metricName.getTags(), value); } private void sendTimer(String name, Timer timer) { + if (null == timer) { + return; + } MetricName metricName = new MetricName(name); writeSnapshotAndCount( prefixed(metricName.getName()), diff --git a/metrics/micrometer-metrics/src/main/java/org/apache/iotdb/metrics/micrometer/reporter/IoTDBMeterRegistry.java b/metrics/micrometer-metrics/src/main/java/org/apache/iotdb/metrics/micrometer/reporter/IoTDBMeterRegistry.java index 79525be24a1f..bc481f8b5ba6 100644 --- a/metrics/micrometer-metrics/src/main/java/org/apache/iotdb/metrics/micrometer/reporter/IoTDBMeterRegistry.java +++ b/metrics/micrometer-metrics/src/main/java/org/apache/iotdb/metrics/micrometer/reporter/IoTDBMeterRegistry.java @@ -73,38 +73,40 @@ protected void publish() { getMeters() .forEach( meter -> { - Meter.Id id = meter.getId(); - String name = id.getName(); - List tags = id.getTags(); - Map labels = tagsConvertToMap(tags); - meter.use( - gauge -> { - updateValue(name, labels, gauge.value(), time); - }, - counter -> { - updateValue(name, labels, counter.count(), time); - }, - timer -> { - writeSnapshotAndCount(name, labels, timer.takeSnapshot(), time); - }, - summary -> { - writeSnapshotAndCount(name, labels, summary.takeSnapshot(), time); - }, - longTaskTimer -> { - updateValue(name, labels, (double) longTaskTimer.activeTasks(), time); - }, - timeGauge -> { - updateValue(name, labels, timeGauge.value(getBaseTimeUnit()), time); - }, - functionCounter -> { - updateValue(name, labels, functionCounter.count(), time); - }, - functionTimer -> { - updateValue(name, labels, functionTimer.count(), time); - }, - m -> { - logger.debug("unknown meter:" + meter); - }); + if (null != meter) { + Meter.Id id = meter.getId(); + String name = id.getName(); + List tags = id.getTags(); + Map labels = tagsConvertToMap(tags); + meter.use( + gauge -> { + updateValue(name, labels, gauge.value(), time); + }, + counter -> { + updateValue(name, labels, counter.count(), time); + }, + timer -> { + writeSnapshotAndCount(name, labels, timer.takeSnapshot(), time); + }, + summary -> { + writeSnapshotAndCount(name, labels, summary.takeSnapshot(), time); + }, + longTaskTimer -> { + updateValue(name, labels, (double) longTaskTimer.activeTasks(), time); + }, + timeGauge -> { + updateValue(name, labels, timeGauge.value(getBaseTimeUnit()), time); + }, + functionCounter -> { + updateValue(name, labels, functionCounter.count(), time); + }, + functionTimer -> { + updateValue(name, labels, functionTimer.count(), time); + }, + m -> { + logger.debug("unknown meter:" + meter); + }); + } }); } From 62f86917ed645996a5ff0226b2e97a17e650bce4 Mon Sep 17 00:00:00 2001 From: Haonan Date: Sat, 14 May 2022 23:31:03 +0800 Subject: [PATCH 010/436] fix some invalid pic links and move some pics to iotdb-bin-resources (#5908) --- docs/Development/HowToJoin.md | 2 +- docs/zh/Development/HowToJoin.md | 2 +- integration/README.md | 10 +++++----- integration/pic/Add_New_Configuration.png | Bin 129654 -> 0 bytes integration/pic/Cluster_Category.png | Bin 175311 -> 0 bytes integration/pic/Fork_mode.png | Bin 188976 -> 0 bytes integration/pic/Run(Menu).png | Bin 99077 -> 0 bytes integration/pic/Standalone_Category.png | Bin 183256 -> 0 bytes 8 files changed, 7 insertions(+), 7 deletions(-) delete mode 100644 integration/pic/Add_New_Configuration.png delete mode 100644 integration/pic/Cluster_Category.png delete mode 100644 integration/pic/Fork_mode.png delete mode 100644 integration/pic/Run(Menu).png delete mode 100644 integration/pic/Standalone_Category.png diff --git a/docs/Development/HowToJoin.md b/docs/Development/HowToJoin.md index e0a1df7cd7f5..3ef4e7f9abcb 100644 --- a/docs/Development/HowToJoin.md +++ b/docs/Development/HowToJoin.md @@ -41,7 +41,7 @@ After creation, please send an email to the mailing list including **self introd ## Subscribe WeChat public account Scan the QR code to subscribe official public account: Apache IoTDB -![IoTDB WeChat public account QR](https://img-blog.csdnimg.cn/907f9d614b2f47e3b0c66a7c53bcbd5d.png#pic_left) +![IoTDB WeChat public account QR](https://user-images.githubusercontent.com/7240743/98633970-73671c00-235d-11eb-9913-f38e570fcfc8.png) ## Long term considerations ### Learn the basic use of IoTDB diff --git a/docs/zh/Development/HowToJoin.md b/docs/zh/Development/HowToJoin.md index f75a4374500b..d877a31540b4 100644 --- a/docs/zh/Development/HowToJoin.md +++ b/docs/zh/Development/HowToJoin.md @@ -41,7 +41,7 @@ ## 关注公众号 扫码关注官方公众号:Apache IoTDB -![IoTDB公众号二维码](https://img-blog.csdnimg.cn/907f9d614b2f47e3b0c66a7c53bcbd5d.png#pic_left) +![IoTDB公众号二维码](https://user-images.githubusercontent.com/7240743/98633970-73671c00-235d-11eb-9913-f38e570fcfc8.png) ## 长期事项 ### 学习IoTDB的基本使用 diff --git a/integration/README.md b/integration/README.md index ca7aafedbc91..7f7aa54fd264 100644 --- a/integration/README.md +++ b/integration/README.md @@ -45,19 +45,19 @@ mvn clean verify -Dsession.test.skip=true -Diotdb.test.skip=true -Dcluster.test. And if you want to run IT in the IDE like IntelliJ, you need to achieve the effect as the `LocalStandalone` profile in maven. Follow Steps 1-4 to achieve it. - Step 1. Run(Menu) -> Edit Configurations... - ![Run(Menu)](pic/Run(Menu).png) + ![Run(Menu)](https://github.com/apache/iotdb-bin-resources/blob/main/integration/pic/Run(Menu).png?raw=true) - Step 2. Add New Configuration -> JUnit - ![Add New Configuration](pic/Add_New_Configuration.png) + ![Add New Configuration](https://github.com/apache/iotdb-bin-resources/blob/main/integration/pic/Add_New_Configuration.png?raw=true) - Step 3. Input some fields as the following picture - ![Standalone Category](pic/Standalone_Category.png) + ![Standalone Category](https://github.com/apache/iotdb-bin-resources/blob/main/integration/pic/Standalone_Category.png?raw=true) - Step 4. Pay attention to the `Fork mode` in `Modify options`: you need to change `None` to `class` in `Fork mode` - ![Fork mode](pic/Fork_mode.png) + ![Fork mode](https://github.com/apache/iotdb-bin-resources/blob/main/integration/pic/Fork_mode.png?raw=true) Integration Testing with Cluster Mode ------------------- @@ -80,7 +80,7 @@ mvn clean package -pl integration -am -DskipTests -PCluster - Step 1. You can copy configuration and change some fields as the following picture, - ![Cluster Category](pic/Cluster_Category.png) + ![Cluster Category](https://github.com/apache/iotdb-bin-resources/blob/main/integration/pic/Cluster_Category.png?raw=true) Integration Testing with Remote Mode diff --git a/integration/pic/Add_New_Configuration.png b/integration/pic/Add_New_Configuration.png deleted file mode 100644 index d6b647c6ec19fa4fee398d1e2c6bd4685e4217b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 129654 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�drxekS0kOttwoIs^9L;Q4$s>ku71Fp^hvF%qgZP&8J?N?J=o*a zBg(aS2`2%TO>M-4`PQ~g0?f2C{Y=AoVuspGr=$j*&h4E_yq6-jxTTm%h$vs(|0;~G z({-Iu#}&%cxbDh@QlRtLr2KouT^5$nk!&pDF9)Q~@YRH-u{Cbdu3x9Xl$m=4! ztWNJ~$KQ&_>1^~C7$Hu)YJ+Tic0=>aqO{Hf+L7l8qN>DsLBb@eIHVS?bkh|l)KE}J zk`VxH1OhL+=)jL237E(ZKYsn68P;F6Wkjycx&0_v5)z?3q;4IjFJO0ftKY*MU775~ zS0yr%w{rZh3nrO@x?p`OA@xmibag06Fyh07hxXdDV(6;je5Lo-2X}PwXWKqgNAyrp z4WaH?Y^h+Y3E-YWpJ*jo#R}lp^278f! zmm$Gz>#^2iANSi5%g2X}YV()zNxk5r2X{o2yc@&xT0DA~&QUN1r}WCiSu zym*AW8@lvfc|3JhC0AdM3!|J%hP|OaCe&X#)r$Yeco4ssV%V;|S~Ts+fVGgIp~ z`%)%g`LpTQz)lRC>@PZU`YTU#ddK!YBHpWW8%rDFZGwcONFM3q`ApBi?0oVpra9*Y zEKHx%+q32RAFve)Im&Eos?}sHejD?r179!!Is%mc#hLC({^3j!hS~z%+oL3GYoc~z zYy0j(1vger3XmTU*ZmGHCAQXWGak)ruxac;EzYRT&gmcO@{JtDJ8|32`=**vqk$3Mc zK$_5(-eK7(o_|6sf}qQi`;i&Ex1!+Y6<2z?(V%DTjcWf`C84}QP)O_u&(VWL8>5e| z0hI2pF1vd6`{F4{0HIu96%c5TmJ{-Kj<|hkU0)$(^j;q~*sZ!t)3-)mkr}xVldk_O zZ$ojPW+W|Gfk)}T`GsgrXNCY5_(5#IHLvX4@bH(bhtdxI;3kdeJ~hsA@;^V3_&@%n zQ;u1D&dvd)e(xIf=#6;*T?qIJaU}qd##c$MbjQ+H{=ek^kBzEK73|releysO9W!&4 z;Big?FkwAFduFjNd@cTBq@aufR1x(P`(HL-rod;K1Ai`2{Mo6xcZRI>M@8kQk=vhH z8WI{gfU4X$?Ctv(rc+`yGg+6Ihn5s!a?4C~pgqP5=&OHKUgbHO^z8%9nK5 zN&H(fp4%`Zn~az*JyP0FPs+xWITiP-9^zZxT=_}F)j{~XiWt7)KrLAi@$!GF0}?W> zfv-YNmZ8v;Pt@VPpP3^%=y)IX79S#?X@$Kvn4IbPa7&=%W%awyWW|N9H98Lr0%m$X zo1ki9GC8`3T)d)KBz^LhK?YM2_0Z9;hGWAf>dD;dEXQ9mlSLwN+nSQ6iOB)|&--e( zS7qLzAMyrePzkCyxmkCyjHpgYmP;EA@^k&6B$)oHq?$_HJ_P;2E>VXoc&coRwTC-C7|kE%<};DuDU~XT z1(D}pd%t_keciq`?TC)M_)DpK;#<<9?BGbY`R;96H-WDZi6<%z8Di1D0cC#SZdoi65?W& zhPNX<&B~=T49}b45wWvBuN70hA9{UY$0BOCt2EoV7Z<}O@#7UBiX@Vfp3ZUWgUOvG zgsP2;nIxYYQI8rUUnsE~#Ag?3fu-fBB2h!mezf9s*=y&nMYmbn5tlxBpLz@?dbl~dr;73bDQ+3s-1?6t%vzZtJdB>(5s8>5Oqw!@U(sO1+B)J5R0nApz zTB)dQCj5AfyhiSW@|S-TxV0*N=<#q>xM z3$O$Y$ay@Wujdf9%4~c>EjTId=v=BLzt;Ov}xPTE`=oRON4$02-Vz;H3Aseo?)nm~P6vEZ|>(*`AGP6U0vKa2Zil%|4- zg;d9zAp@h?6n7z3&;Asf;;F!0zyC$oTZcvYu3f_d0}MTMHwX$6(mgZ?$N)-6H%JZL zF?5GPNr#{!-Q6uANXH=EDGh$N`+48}{e&*W2;`LzxBc81

wWm9e48h`=Lk79gs^%$XODO)aD7L|qwzF$bVJJP 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

0BFST694wBJ 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γ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�SE017gs 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 ZpPz8t$IRgSfF}$OowcwxuQhdz{U2H?=4k)` diff --git a/integration/pic/Cluster_Category.png b/integration/pic/Cluster_Category.png deleted file mode 100644 index fa2531da019404fd8a9b1043369fecc34f8bf701..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 175311 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%0DZe0>jAI^4e8!41-( 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¨Ex$w&@>~_55L0pZdDL-rZQ&5EE7@ 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 diff --git a/integration/pic/Fork_mode.png b/integration/pic/Fork_mode.png deleted file mode 100644 index e09e06672d668a913615efa7294ee4990775dbdd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 188976 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¨8yVSSP`B1|D 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

T3n6YUijHVq(an8Y$^NWx6|By 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

o!wV)pm+@?4VftZrb5EL}_&w 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 diff --git a/integration/pic/Standalone_Category.png b/integration/pic/Standalone_Category.png deleted file mode 100644 index 9c2d947d4bd4c1db29d034756b378e199692daca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 183256 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 {i0OIM|!^cLS)90WsFrKhWwK~ zpcF44{yIWDfIo=`cckitd(4LvE1uko%JI|}Z7?3x*Pp;<`5Znd)9rtHPb3``;5V4n z>~?Q?yzt7ly4r@dwC2fuPn3lU`cg7I^7el6WGbMwQL@tOJ#e77>`#lN3DY{4#WuAtJU_V+Hx_{9p6yH3?Xcqx31|d7J&jnGg{qj0hp?M>pIB6T0 zR~{?Sk-fpWdnVrCyrU>>z0u-6m1*nT0=o=ir2U}WJ-aX7{~~yE^Im|J_&S;}ehhOz z2h>NCqTj_KxyKDB$R5VzuzMTEfcz}*R(*Yl_&TrZ-^P|jzj-$h8?aT?RA+m{*HV&S zWCmFj&lQ8!zH=Mm-4Qf-HDOdUyeNME^faKNAY`@{QjO z%<7pEtbziZzxN&es55aTVoSQpY5QV13T^7P=2m<`CyRx|yhml$@ka5in5fBoNvS5% zBItT5S7HmHMk@~fiYxd1&(7CTy~-ZDnogR4)wZ;%#CGKp+K$snj5!h{p!dVVhwK~d ziIZ6%mZ8Av#egcOsZB^X-O+4JU7*XxSRc=aiCT%4T8zG!tfn^vUZhxj;Pd=#i^V5| zn8KA5Dyy5Gv?~3&kMF)5`=X88d*eW5KyOmLcI^)K`|KP~`&eqAg8bihPA80FsosrW z^9x+d8ob6=J_H^%wTdgQzgmmSu z3>~T+#b+~X3x|tHpud%!VE3i{{U{!Q-;f@-t@d`hETGaL{Yx*Snd94{>vf-qn#Y)N z-}D;ee#-zRAJ`8?zYp9SdBBQcdMMi(d{1mQi13DpB&-k3@uH=NC1a-1*&h`MgJ;9N`xE z=bLH)!ZgSeVoT2NDgQEq*Iv948Vxv@4s?15;fBnavFx^r8QeUDB@}jS2f2v}yXqc( z5IlMh;!^Q(ul4)PZ98e+`-LlBHkyUXtha2MD!Tox(M<=N>-Ih;ia|Ob^@LGKINFb# zQNe1HNA0wreMn#VZ@YfDAeSk>q52erIMO-M`=~3CB?&=tYgV~c>FQ`P$bk1ZHFmfR zPS+m|sRzbmE;l_gx>e3qNU@>VWukiPTFcse ztcOs;p^UughkK-?T+wGJ>s^ET-a0$FM3=H`-=*qtM8{70YcXV)=#h?$63^p{Ltuf~ z-6Ev21MVt9s;&6lXM4OF#Hn>bZ|3vun1F|}b;x+Wz!S@k(5F_ z&I$yz>i;iS?9iT`c z08QZwhj(z5ON{l?fN0w}E>?nP!$MvK%ktK9ryWxP$^lT-OsHN*Uts$Lwxm~W2Wx={ zp6BaBx0K|L$R1F|=YfWN36 zdIt_RN5KRQz&WH1?Vw(Xn-5_S+`}DyN$1$-w-;YmV|cbKD=t$&N8+J|XQ`r6UfiFc z5c%uJwHtq~KSiG+V(d?(tVCYkB$cL6kFNHMwo}w-^S;i^t*awRW%kR^%w}gsu%D|I z@!Uy0nQy&j$QJRztYI!njK;roXJYWY+^yOi1E2gpX)YX@zE(aO*G;jw_2rc0M! zVAE!&y4a;6#j!#OM}k?IaqRfPxU8o2XhpNz`7MBIio@Sr2QjhlYnNk&@3_Nz@!UM| zvw&QHTH$RFJ2{?Ij~)qj!(Py;vc}%zqt&D*cJv@lb-ARy>vwl)myX7-F3h06SFRJD zuNERaxZNw(vNAK^rRw@xdq2~9tUJ2#sMh=1uu>x$ve=jq=_1RjhkC)Nh$HH>E~2p= z37sjpo?#7?%36Zz;jSnw^LK;JeIZ53rA#y)a?X8_xXV z&3%pqUe!-PZQ!F;Klnfnz}Tkp=zuoY20cl=%v-8Ox|9&CrKh;4%IIIJw3nnTs1~(z zVO>@mHJNT+ulai8@u=khD-+tCP~b$c^|4Z&nS0>lkhB+vL*gw-!mpMmfM~3}!k^Jz z6Ybdlc_}K<2nskXyMkyVs6?#7c)QB7EzP%F+FyXzhf#TGZKQ(u_&KoR5ZpHAWC^@+ z3t#T%Oc|bx)VwBtgGNNsJ&xf0SL^%H)IFK>xR^@rF;BagL>_*eCbFpwAJogmw`oc~;y zUKT%ko-W_NUp5yQ2}VTw5vC;C?6xs6dCRa3#JEA3j^jXt|N9i70*?fWW5IhbH*=!mw(mXTvfcnOqp)gHd%xVB4bV>3SXI=aQ_>XgD@UCbhLMyX;h@h z2zg#E!BiV-57&cgvb$S#R~Drd00nepRWod~bT?XEBCW{$`n2^o5xxShmtsZ4db|t- zCROW@OeNHP&1#b)l0iG>S?z~rHc+n1klPDE39P&fAeHbE@}N6$->8{g?v{+0XY$}x zHF}-^NR?&Nn1~ij{Y*-E=V8h+R;bVqRHDCQ1O{A{b$u!V4d6jk3t&^FUtmgTN5wXu zxK+baozcsVr(7)_Z{}pmit|VLgAb|{F||M6dzkvj_~v@%5rvN3mTsm8oPu_fV075M zrcwFQ&7AHL@AsyVN%@Ff0$_e*ag}6s?wa63>_E9yRZ=PbkgLjk2`n^Hgof@=St@VR zYynro=@jyBvokydRQ{g2?Me+vsx^31>(BWz@0#5XqOa4+a9w zFVlftqD2u%7<7w5G6@RtIFFv+=?Wo${!evNBz;1bCF@uR&lhB2_LiFH$aRS$WE%Df zbh^`lBZqHh`i~~{y|zMscKuoFLP4#qg)kB?FLS)nO)$5B^*2#2Gak?ZP`263bIV9U zUN-u&3{|p3ur7@j8B#U36sZKqlX+fmrsf(G%5w6Cbs6#l)*YQz3;u_ZmthZ}PAz(K(zs#9nMRGdHeq^pm{^qxke8T786|Nz9XN6ZFj^&(0Bi>9 z_2}_(-cC*m80nwq2eU+COHsxfY zKHx<;%AASdHH|7@jNdAc7P_63@vR&IwRyq}ssXVx8XFon7u7^%jO5ymg)~a;1pOcI ze}EfQP7Q!px5rB{m+7KCF-Ck9H%{M&KYbOGf|Q-Pch2zfbx~nJ^X+Julp?ez?>n;v z0x%Oqt_z973(xSM!^?incX%XRk=!r6u1Y9aKf$;^Uog%&DKy>>U0CuvAJX%&toBSI z1N~nFvi0mOOn!UX$Zbb56v3qA0}-0wKSm1wL&6smyi z!jlG?XC@KdzZd&y#6e}P17t8!!l|ymxt(u)Ca}FsRjb)(IIJNuQymhf8P5vi)w%vg z5x->-f%P~T-%Eug8n{*@7FcCDNRN(eDox8dQ(mVarQ8fHZ+M3x@~)Dy_NM;FGBJ$V z#GOq0{gAKxjP%m8<8$NwS$+)+hpD=|^w)UAb*t95__cFb`Hap(v5J>Td5KdfWVzbT zp04cHtL5?cRtz!9okI+L5gYiO{kd|KIJO)G4}2vKEgH~kSHo92${^aGQO*O8TlUjc|`s}b4>6g=wv-p5c2D;wM@mO z2ljsU9yB|L(HDH%@DZdWwAMiW|ccxAW3pr4trHZo-$7 zUHMC+{!KSCwXv$Rh#6^5ww~rzkK#MgSUPW73e|lbIs&=fN`(7`;ZMxL+JS>^Aq0fG ztuFsNzeTutz5y<3?bheWPy$D1J1B5rg`a80myl8y@+UWDBN8?hBlK`Vfm>;4`H-i;B%!5& z=a|%4y^KYpOO=-HVoyQv;dD_=2EayRHpAN(mrp+X8xWH;H^HxRn!!f4*;k&L#HYjL zq5V=ut~IQy$T5ytY?Repxoa&S(RZpvzSBgXDr4xIICi z=c~V&h9iAhwbbjPyw~xzj#+HUdU7WT;|S!K*eFQG2G!S3B6C>Fs@a zN33<0LHzwU5M;uw6yK=coV2Fw{LP*Ggms(DLbW2fg|=l*R^|QN$p_&hwYZX`nj=ON zyC%z#3W*O4&&m#r^sEr_L60cbs>^&oED-Cp7IzW`PHIG5vwq@qJb# zNSCfCAv-`4wPPlitq+5L;fzDXb#l;}_h|0e8DnR^a8g^rs9^EejijB~@TiAOMd8c+ zf}|=C(X4R)L*Dye?pG+r?H@~z%qdOwPX6=I{d?ZyST^GO5q1kL`s|3IDDTD3y*q63 zSp0Gx#AgibnhwIm;b2YbTFTy6iXvAHX?Yb$%38%oGp3qD!yqI0`&PbYRJv{o|xlhaRr^G}M)ndU!^ml(@eKE-f=fH`b zgLgJw;)Xj0s%uB}i|UDn={aM1|JNz^gSsrqYqobSf336jF)9a7+mm1~3L4^+CD0mE zN$LC0Ms8}eTzq|~eUjM4g@2(*^F#bpV`}{V{F|sWTB(IPoujlpQ*A(rA}%eK7&U+yU00X3j{!59a`Y>ZP0mZ+ z=u=f=kfEr?LPB>E&gFr>IoXwA-wXqF>@*jxpCn{N6vbVW%JwUk0B+eB^VSw6d|}>0 z$h}*e+bakDqDma0{V6wxd--)SO$K8xoK z^HN64O^L7HJ~+M4yJz(K)wC;hsUkX@;84=6I07cpeLZ}cg-mVfjh^Zv|JExgQ~wI zu;By#J3sOUQ4@ub+n>D{Deja5N6GE-A-BuehSMK}FYO+@xwn>r{?)QGK17^lYNJM=@5MWr0cO8{9lz^0K#YQK;oD5!N zk+@+kZA{~gd@oNV$h?IDmhP@G^RaC!&twncdO2TMq*eQSxN;vtDBObqfLS_H^d{S4*EWkORqO^$1Ge3yIYXqj?%7jG@PWpj*h5m4=FwW?=&t#~~OXkStcP5iLW;rlazLHit z-`6=VOFxOeqv%u_?^0^9y<9T|Bp^s<1ouYUDRPV$MWHxk1#$|=py8wcN z_yvjB@#tXd8miE8>_;ai!LTQ&RTQJ}IVTjTO5i37Ik||Q9JG_Ce|cy>dT+)s8*XVD zrjKlGptPgjw3}4@?=~o=nSy9%=_#8YaEO~t2M6;8`oXDniVdoua0E0$b7-?xWn(Ds zrDzp8JHf`@h&dVif4o6ZFnv{H{=TlDNNVS3vp8;a0_-NR%kCg$XPth$(BD$;=l@yG z|5&@%)C=q^shw`4 zbO?|KU!n_$74J!KkjjNEc}T6EPW&%{L*Qy4@cc~dh34gIBINEqa$`PC6dMg9oBQJf zYB@8r`q4$fBt@6D-ap*gqTM{{w}L!nL>JoWXFe3Gw)6J~ww{au*eQ`ph93PjmdGhpBVp(@a+PKi^C90IACYF7RFR3pQ zBSE@3!)^B;oCkNkZPX21!}NIp_NdnQ(C>9_=uef3O9ewW9BFjX9_C^3FY|}|-wRsTqT>Eiby^=IJCKX|;_Tw_VuqFr0(O~>wI48n9$Cp?H z_vdEVX-W#aoqKS&2kD;^SXB^S9y~t^gz~pgs7A*Ji#1xHBi%qCEYfHwd#`VNdTKH? z9mzw3GnxVu-{;xsj%DLwa2@RqVa%MEAWy!Btmj|6(zSjRdLumHYRP5-r7<;)C;T7! z2|4tA%r8ohmd^eC0+1nax09&sEBqdLY{zQjYF~ptlN*;&Hlh2Vos&Nq+MK>cfVshm zk5TVj;?yRL^9_$hO;2)x9Lp8dKptm%pNzAqM9DkJv!m8Ne%NU04PTemeY3kwklWA< zD`U#87);YV^POqjcmm069!rlOQ?7J3d~f=HJ~0SLB-8=JNy!l(4(|j0|679ei{~*Z zH;UN!NH1tAViWXu+bbsKBnDRBFq?YV8h?JQunUWz)ZTzQ1bV7({*lb~`Ax1Va_6CR zI&M%syb=eHmjWXi(4jne4%NL6>j&8uk$WnE1^m=LQ7Mmphb@JBL1e&OrEav&PDD}1 z5aI7oirOER8hLP?BjL@%=ZR4Q&Aq`zPjlfv{33MRiL)voG4|pc!VQC><7_`CY|+%gmwQXD)(2=tWS5|5v3i8BsruPQR|Z_T=PF4^z`-L4_XHz zpz#xU;e~u9kg`u*v^}5Rvb)mgo5qS;(DX2v5_sd}Y)Vv?lE2hP zou|cyHF?rXGK57ErRSHI!hg-@3OVCphVM?o4SOFZ`MWb2=N+?jWi9d7VVTHufejWm zIq|R{rgG&xRK_;!cPROWabG{VQ!!;0j;MrozQ^s^2`hb<9efy-Y*l$Tf@<}|A!5pd zqoakY^MV^1Jz9tfb997(s`L=rOnNj|pr5#X#L{J5Tsaf9nS_eZM8e6c!}zoV`na5%@oGx~cw1{;RlCR3#w{Gp#?ALi zph4wG;P~Xe)x2i!V{Q854~xB$2;`N!ImnH<{bn(!VCh87=M|}R2)?xSbYv~hav!T?V%XKZ zq;u|w)aoD1<2C-^5CPuhP7Me1{mF5M_#i%X{KkWme2XhRK_x!3C1<6<^ihR8qNTvK z&gM#*bqH9<0fQz-WbQA)wu>LzbId95Egckr&{DwZ_yKdbB604`lS4R~i}u2;rnl`6y$Vv2jvEKN89#hTOw!q` zz@Z(-dYf;q*UBcw)DrRzu5kavE8NxuZ-T|8OLv;EPNN>v)1~knkR!${r_1tEpLg2v z0pLcD+0EaNwccna52qUs!T?2nVWq@a^QypkNqxl7x7m5joV4=?q{PcA>mKaak0o)4 zi;=mPwrVh>m~JmMO0RE3{9|}kRM!5!nsN7`8R4@yy3_dgUs{kx2UMWfe?e`)yn4GB zJ>K9H_l2n};SJVskC0S^`S?WlywK-wid69)5r+JQn^-L0&f7>RNrX`&!ehIH3=BL0 zuEg=^B*9xGgz=b^;UT^CwH_1dsUGy0F0AOIePnd?wW8M8X>qDjLpm@X3Mjrsru?HJ zZ`p1OdI-Y5_o1oP8TETs^zGO)o+J7Da#AY{jbyH(Y4kFIln z9%wdu?YrnsbMhuHl5_64-Q!^TesR{y)S zF#o%*Ey(&^OqX=5^ul<%51nv`IMRxo?XomG;37@L;{K+~@1BAEt8W@Fy04%!&#R@B z8oF=knJ=da&#S9caJL(77Sc#hC0otKs9`T=@Wwei+eMo5SKlmFIA1{qj84L`E!C_y z&;ZNqx#*>|8lTXRY_DIsPqLQ4dsT5|RQu1HH}l3any5_rSUOnm#XziZv-gNiqF3hK z+el<5-$;+X`y=|e3uadeU(8*2K7HmFBp8Ci9CUdxb-M-H6Ei z{`%|d!lPamd+Bck3xI;e?<5wWQ^$W`hHyd}YT!=DW=3!ZiI=<6fy3nTqf3`l%p)>M|8U<9i!Eufk9YbL%F5YbjDu~g-o#ny3Bf1t`a*-%;>WfEf-u!p?3e$m zD20{JdtssOZNEM+p%4TCZYR2MEM6|fJptZA7a{i{bw040{{G(DfX_$J0Kq4X3(i-H zBm(vcoOnV}QM5R;aKrQk6v3qVPaT`kR|{CB&+)7~Alz*?{j}|7L8o%>@+cfTWykh; z^Nwy}-{SCIXZ6ic7~mayiTWzAA?a>$gFw&NI5k$Xhr#W*dR^A&L>B>J2P7QGw(r`W%}C^eW^hg32D@B3Uyc7atjy6c%IwC=ieBcF9c z$ang3V>*=ottOpxtTP8K@CUa0gdld#D+?XS5H4V@otrR0PJ{eCGu_J?K?3W?fL5gR z_@mtqk9;ngAE7Ke3ZFs$UIJ{*L%UCfAyazr@^;@3Hw`ANLiPZhmH%%v? zcy<;3{A%9$dcWHRf2kw^NFTW;6(SUR2bSF5$W5PWN)}WLGPbAW#!+{wabV;p_^*lA z|Lh&a>c-H`5l<&VLq|-S?n7mDM5Q#>SD1>f2RX zY+~`I@!@ht74qE>d4589+O*cg*27Kp3(i4zr(bb*T=TZJ{MEIrOi;~^-`(-++Y_0? zNhtd40!~Hmnbb$U%8CK{`q>4H;$}{j6m3z+t82CPtlUXSSx=@$WMHv-DVq<`TGYJ8 zAJt{lPI9TMSvjo<1G|a;u$6T#*s$%}t)n2LFFOp5fB_S(3($3|s>VCBNsmgDR^O;H zHgCX+)V`;63fISpf2n@;U(Jm*kI5J;bi-~Nc7L5~1V`O6IU^<&JyHJBtbq$pViK`3 zhv(y8nw(wBsc3}>v$YgEsrXvd43xB~u^XM9T@7J6{-g>GMyM(i{iKunxO9DpFf?K@ zu9Ddx!wILs;PzP@T|P??1?`3iJ9sQu z0{utq(4?oB8F7J)H(UdwI|FJ*G9^55&rVHPDI)}$(EFpiSwMO7s*koEAyNH-V}`J)j}lr?@g|E zJeuhC`Li3=$@W{pMr&c~#ok2Zjb0Nc6VmWNY;3c;qmj@a7&~VgD{x&q@=qnUl5b>s z-j?dRi#o+Oa}I`V{vQf(qvU?;-mqhURZM_wY5ZQ~WA}5v&AbP7-kAc6&76+X{c&31 z9bSE;@)QS1EshEaqp@_IJzDVpKMN^-q8H&~gwH-*is=M#K?DATJ2=sz*|nH1%4vOx zWkoAN!=|?X@m@2U!A6p_Vd=J*&Cf-O4sPhXprG8G|3XhxcFqb?Ccfg46rrHTrieul z3MF#1@bst-+vhM{VocA`DNWVTe;*|`x5vnyAN=4iYe_|`Eu}tlMG!?=c{JC+ZK1r7 zbO8#Hy0V_M?+ag1wn*Rw{s54@QL`{CP-w1|El{q}Tdd!Nen1WxnXUD;$bNpZs9jYn zI(NAbLhL*MTv#D;0=G}4{uQ!pkYWiXAl!hM4_0D23k}_>APVKwSckNipf67!_!YPV z#;tlP0Vf}M&YlqYr4Qj;yhrN00TyZ(k`@cGNN{5c;?!o`i28sZd3` zCm0iZ07bWELi93nj127IV_F8^gHc&3K!;a(QF77m@KKWo?8p84llh#+kp_OR{WU!L z1{*1cmb6HWC9~_dp}9M zI;!#t_Y8-e_l$i8E;C6-54?ZwbB?kJamwo-d-=EJ7gVPgoGaNl@aUjFdcR*R3zLL; zw1?r$x}FvJ%2FIPrDdCznf^#&Ga+%v{Q=e<9@-Z96iwtDf|YZ`7YKAy+-gFL6-_kN zIC0sE)r3^LxHF9-k%YV*cZXci=L>h)>Su$9weJo6Oj!6D$9| ztCiSLab=2%Bn;Zlt#R-cLza(Jyz{_DHZzsE_py~{6G)AZBwx&po99E>j*1?Sy%;JI zARHwM2uLBn^pO{mk6`LD6i@TLaW98Nn?)zZcqG5-CFR6&V-Gq?Y#tOT@$mAg_uSQD z*)=z#6OT&IWdA*;B%vPM*%sbo=@-q+k=!YLtWhOuc4Q`n?$e}(-HDd``xYHB65GJS zJj#aILXL5&Vu}(?NV==A6Rqa0FSNH^pi>0I<7+sJWR`8MEip@veMlt5hCh5%D`JF-w_Z*%SKP>(W4{afmDmvrKFvR1J-t?hMBM7q`!XpZmw)a^x)RYCa{2&n?|rFtQ|$@ZnLnf#+V}pfa{mV@C%yiHhF^Hc3 z3mdZf!iY8I@9ixG78;Y&KB6G*-hcPh{|7}BvHOMqhA$YFf679U8Vri~IW|rdJx!Kh z2ELzU>qPpm;j;36sh24G1;LY1ymf^nfLtZ~-9(-mq6G_PZ33{< zz%Z971}#SCVkvgOC_84*(zfgc8n)<1w-&ROL90t@$xEfF4vSy2UX8(8YYFg3R0;6A zLy9{Ry-21Q2t$_!#Hs^_-+B&}LAa5n(q)Nz(BREG`;TNO#991bnWIPgo71cB??~=? zNr(@k;{j*UR+`QC5LgMn3>!|*Wxq^vI(|>0>CZMDvM*Cae3{NBpX8;$uyT|zRyE^F zGLH~T12!zNOB#fx{&F*k6blDdXoZ*=I+#)M%?7r(+}nrRDa-6m$MR~FBs^h@=jzgS z%SxE!>dN);*VIfECq#~K@@kYNoT#ZPfK$7e2|0dF;}ko{wa7~DN%M&gG@3{KSY{d6 zm&$nb^ccr&#%cNC6u1@dvSZl2@w3jXxQ5)3LWhGxd1*2`fp*$tL2QslRid96ulSb~jCtk{Eg}^6iMyD4{qeUKwd~B+xTX-D=R1;VGLY%L! zf=EztqojcNXZXOkJZ!y`!R;y-LW9|dhNMoI3cFsVd1Q$AAHdo-INC?}RY6~$& z+nmh~=b(z^@=mk#Q1Eup9U;LtT8!0bPn{zkUDwx*?m1k=I+xWlQD_<;(@m2p@jskx zj?TPN(J1AA0po>-2VPgbEgWxStpWi30T$>Gu0n*%E!I;3dzzy$C~t5-;s&L)g1MYE zGymY5{6MVGF)8CT*ql9|F%@UkOwhKjO#?pb>$6^ppZmAJ?TLy$7i^JR$^}DuKy8$b!{;H zfSpE?e59Urn4$=9L-IQs0>8#*=Ur-41MkSs(o=&4cZnV!%u?E_&9>N|z*xoE^$S zZKPQ=44iG(rp@)U*O7C^B$4^D|0qY&ZG|=AO`1Oj&AjYZG-WgKDS&3~DsOv`M5ufb zEG!`}t8i%>9fj7FJ16bK(dQf)@EuQ)LaW;2;E)er`P?3ptcHOpFcF!uY zCGWA1VJi)F0kjVeKXB!Jv}ODe0H4q#&a!!!k6g(8hmaUZsjpp0tX`$geF-6?rBJgJ%jAtRynFW~rWwJla1Bv?8$9-Bf7MA9>mz?$0LogK{zM z!PU~@e}1M!#N=RQ3VewTjYp%WOQaTAZ02wYBZjIL z=~l_wuT9L&Zh%kv1%Bq=`%D2mPVo|53@W-M(+jK!aEy(a7oH{lw5>}e#2E3LVK{1MQNqFw_hV=?ZCWayEyYzb#D~SN8)E(tC!l?uLdypPkCM zIt}@5qrr#A`3{@f} z1oN!1o9GAkD#7*25c_1gliz!P*%qa-&X6SD@;Fpd@ihK(|6_s23)wXSKU8*c@m?R` z`R;AH65!*r9(`FF!QjpH>-!oB+dKBJHA-4~{$%;i{Ho2;`9hl5o0q(s_DNlb&OOJs zp@m{8_;%McQ}fqk4m_*q`_3M5q=P0>mq+%VyP0}eOwky&l`?Mpikr?u?W4Ueee%ub z0_VeD@5&Iw{f}CHUTy~W_Jh8!eDTC){-%ji*-QclYc-B&(9^norDKI@T)%tpE>q(UY)@uQhZAHDyt}yVzIJuxy6BS1mM>7A z&#R}RqC)ea-gWGdy<&5^i&k0Q4a52dd*- z3FJ>}$IcC4fdenkEh!I43&3YL%Y7aE#4e_Ujt%S%?lCtt_ROpwxG7l#j z)1dDzf0^D}P?c!)H3&FF-BB=BV*MnZ99Jb!1P zdcZlBbI?M(neJTXmK!4YnjH7Uz@XWHZDTW9T~z;d6b$g6i^76o8llZ!Vf(v8kJ3;R zs>U`I*1(CHE>!@T5gAhk#n0ENkaLcg=Nd*r95yq;6VVX-J5vpCMo<9E^`h|g5GHv; z6=0`>cZsL|evtm5u=87#loTo1z{eiEJs*U*spz@s&#(>x&i|K(_t`#gfi*=hAAZBz z3z5sYZe{g-DFo(dYqcc!D;83{u1wPY>b85yE{J0_Kl{;EWkayGQjg<>OxV>+kSR<< zj1&Fo4E3E_6nMf$B@97>ao@m_0#7rPQRjwDrkR<~f^sW^q z_!I)=aVMVnvXn1J-3!?Dn}Hg5pK~rLKx^>Es&UMTLWfo96>kNby2vAX0j(ajHq-Jh za=bjx5xMF=6shvM4X}P>YXv?#Xf||(HVmcS`DyWdWEIH9j8?C8ve(;O_q`_fo~k7A zaaaf4TVEJ>Cvl-le$F4J#p|b(*#wSw1S!aQimH~3i_^>|i-N4P}tyEI&t|9W0WRSMg;(Ce_YFmCFQIRq3 zqkhow;f%ebr4-zW^;Y@i{F39XB$9)^ovRcUOlK89efB17jDhr1rR1S3w~zT^_TZVN16K`RoNUmrVBVBV{v>4qIWpC)~s_YuWNIqE6w+zyDwYDQM{ndUp~j$ zZ8CgRIpMcQt;J!&++GYW%(^i1W|em=4)}C-G5WBab*C=y2U zgM4)>eL;e;@(-cj4^i9hdDMxXna$?XpO%Z31{{W+8BzA)-Lm4Q4xMmz{?r0F)#fX_ z2n)wE2l?SqjPG6p+~0QK-EH=l9ar#nukRrfn-Bk%O}v2}SLI){CFa?11{I{Ps`Wf3 z>`$(UN;VW-)aGVngI-qRe0BAANSK^py!-BT>2Te;OnA( zf@%gnsq?TtT2+X@}}1+x?IF?9fyAfUJI3 zqp@c%$=E~(gL^R-{)i2;;tjKysflj!}s0M!>>q>r)o4d%pusVwP3kOZ$EI;QN z*JEh+YGfMi#{a>raQ-tVV#l+%7gwWtjaehv|6DkF=@Vp{^xp7tEC1qC;?`y;O+Fii zFDh@W<0tyviQ{TxYI_);&3VaJLX}at<9Xf+k6Kz08;V73oo?;q*dNM<#XjKV_>b+F z*h@zaSZb1EF4uKn*cF=1S#74bb{xtFy-MA6Y3Trmy8K~&?90>EJ#M)H(MU`nSg{RS z31-iPWlkb#3fOPaB3l3OYqCg=*%YDhviX9oh}Qp`6|ftPGTRABhkUApMe6cZov)~XM97T*LL30HA_0uw1vH$c3Hi*)8bMQUnCQS_zsUYf^=8xFng|}_~tq&0mv`|46O{$4)!VyJ9`lKYw zn`hK*HCKwnlz@lDJV$@E&4%-d?pRj60xSQ=N~mZU^+>{#V<01*(;Mm)qXY-5utr#Y zgxl+)>06Pbvuwfixfve*I~BZX3PC#hHs8Z~?#qEpx44It@eXKA1KoVRnP5~_^B>1b zI-H2@nda!JYNL!1b&FFAw5ox3$5diUNR;2a64gL?qLsN|*S5&{wH4~mtK?0jXO=@7`*rZvLenYd$(pam&X4U6oi5*Qnhe|L?i&Z< zIjQJ(`%>-O=8it*Uw}*5m^-V#R-P}>pC-MU_!i%(HpSUE{xLkcY#VlGHvjNi?c4JB z8Knn8E}3n%qH#YO-Ul66I{qso(D~sgqW}rPn_Io*A2GsbH*VX4-vs|zu=#cC2Ez2` z<4E{n`X-TG=MacJsWHvoK6aPpNgNV9PJ`=^NtUl4IT{K~R1!9ux0X%i!N0x~K)=rn z+hmXZz&_9!8ZG!r!KC|&`yW2+J9}Sf{}`dUh2LthY2*MXoMyAS)gc5pAi~Ji2$(B| zl*eAB-LD=PyNn>0lnAgDvdzOqXZko}!5&w~ORHa1g~f^VujW-hp)Jsg<}HG4=596EkNNIf6M-lUKXQb7 zT7&=Qpte>}{bmFJ`bxM=?-?7!OfX2kE4&Uq6CHAmdiQqJJx}X*^`2>EXR@iD*ckNc@ z#F<*M;IY5{DA0b0MJNb*Z+FJ~Hz65!uYsXk`;0;&C0&%S%NC3b+mLLU#xRNp-9QBr97%P&Oftq>bp!^vD92!lb231-5q%c z#NyDC{*ufl8idirt#+q#5!KtU5hy8_K&>?9ySZ5w<4hTTid~Jj+O$S;omB>bnhJA0 zZzQjE{!k2az`$+Bd5(vC+RQSG73tanNRd`Z-TvYQCCC@2-7O(bf*dIk-B*hCb3OaT zT6JymdWX!cdPvLR*XrX{V;R3EjV*n54{HBQGiwkiy5t-`Dx=xom_%=&&Vwkq+2D&S zi9q&|qK(kid#;W;WnFz`8I|0lMyhmyJKD^pI?-L;Pza8Yl5;fOu6+>jtThD0vA7IoMG5p}WD1g<#NmfnQMbzkw2+kCN0=K+P7 zQ&eqr#+5_Iz2R9~tGVVg$y!WTAg2k#P=F(l_LQKpnD2NqYNqJ6j+QqQ2m*=sAWc{& zPLfz*qNEO_gkRqJqBDSLU~PsTH@aApy>&q?Nu`t4A7y!)Wf%%#m(Zfs-6@8-GA!XKcBxqTE$m$a-17CmnXnd3 zQNP5u5o@kQlvQ1{fbv~lrJWa5+sj>%@V=m#G3kV19cRWmbn%Ql!Jas2P}yBo9PO^F z;_yMy$i$`gERW=kX=H@@E#!$rrzac3J4tU)$wkgbCtHib_n!7dWnvHBvW!g^dK}J0 zLktjqx{};PP|3AW3*Rw*YcCvF!k#trZ4-GsJP5%FI_usx&s$B?TP}(G9@iAI}Ls_+JAa7ISx2TdXsNPVW~>$;hwRVCHYQ=D&(Ca+QYI+yQX_>V(i!Tc4GdU1vK13CLM<#Np+2uAG_HGYe5LxYg}k zTtJV}g2Czj0!^ps0`2y6nRyNr@GkVGn3CXE35zxWEe#VqVKmCHvTa01Cg?~ z(g=H8MO@SX`Vr8guN=gs{jAV~(!flZaP5c$@)G=d7ocZ)Cu^;;R1?(-dX3kwBIjh_!8nM_^%e3ZT9yGERnqqOa*J#tW0l@bq1)suR z6=uX<@muDEa*y1Bv_a;o`~a1@R7HruAaEohhqcp&m#gDBnC6A!-96p0xJXXTOhE!z zGSgcL)4!0*21h(t=AqkoKjFtsSEtlw0Cxoj@R zo5vl(tU?|}j@96#{z`z&=%GQ2^Y6!b`OBaR$odW{!=#>oev6&DU$=du81 z6@0Y!AT!+ZyAhdCvNUcgm6~h&Af=SYLfIX_FtJ z*`S%Qwg|Ikudyy4N<*{}|2;w<3FneC14JvV`5Az_ZSg1P@<5l88b47hNr+(pjc!j{ zf0@zaj)XV&aVz0s%(V)ITi_|K?&Sa;EG}VvT%f*|#4*y3RT6I%RUKwPq_+%$qh@}F zTVC_V8J!ahk$NFaC^26}E0OFyacygZe!qp~WxNtbq!OZYD4wFs@ zBhX*0Sn?FB2fV8U-WHd!3)nwXtapx4XF2T z)ObAEZdQ7%p|^GH|DkhI+1ZJlmF|Sp`u~+gZbzS!Oa23TFuw43^SKuQedmfA>3^G8 z^MZGCF@5N;Z4Dp*%ZZ5i={A(w=qv<@@+Kk`C8z>|vQ$R!5-Eg#JO21R#Jf*u+O_lj zCo!;nMd_){9>Y2E>?4+ZdHI&b&m$n+l=k5a z0S+D>K7<-UkQCLnZBo{U|8M0pC~JTBdX!nF`^jzyo>kOTBi-EZw}lrfwY-Bv+@HhF zhu;>~)+*v1ZQcn<-6euixxZD*l*Sa&1?Zjjm=d1-(b$~k1e#r6l2laZk86zw6-Kux z>GP_pG#nkTcCHU=OYXr53B!5P@w+hJj2g#XOIjM5IIDTvqG^34R<+bL?VXcFr|BsN zy)S{G7zIN&R*fc$)2eJW-VtNztm;g9EF(K{iJsou^`#QLf{s5@@u{U8lv2jB8a0^G zJIORtJUn%_Va8YqlU3Ll8ti#+kRNkM8g$;^?5Bx;8q*#2;Yej7X8$tjy*dP#;BN^& z?T(UPv@CS#)3}I2^^>7kCBdt>|?z+m3oC{2Q!jOwctvNCl`AE;XzBD z(pSGa!owyaBbxHkL70&hbC3Ac6VoRh1e1QODOg%hmcEIXgh zsB|9k?lB!+-`!vsY*bEllDvX(W(S~?RWFAeH-{$;Z{||Zo~+t>C_$b~>|}?Aop4O% zp8K=Q@hpApDy;DTov#Mb12y>QFeNo-v{l#3oUp9$_sF3bUorNmol+k}afK>V%=7v^ zKg*)3t>ZnV>}8nKiw@3ew;-84v@r59yLV)t$}U>&G0`7bUfHd&_e6L@D^xj$Lc{0t zD_nx}uoI4M?(rmOxjjPC+H9VdOfwGGkG?4H-UnF$ISM4m#=Jv0U6^!grT2v#YjTid z7z^F&8V*->wOc-ga2d4wx4Ew2B&l0^2ObVud;c7tv1iN%{tuoIbL%ONC>qu4St@06~% z47_qx9jD5swQd@t%r-Jc%Rtm1qo*nU=g&rlMyE}o?aNPnqU@U_I;X`t=ag8LV3Dsh>lB~{nmW{(eGg3@%OsZ;}E$5zi)i&gMypAyNN;U zhx=`Q2kuYLA_##Wpl@9+@*>sf?`~5k<36m!BOAzBLO{xJ{#hj7o#Ffg0(zALU>nrb z^$*B958uL`LFYI-CtX6o=d7EwoaIRa6G(nDYnC{q<=bIrn7AMeLSDfRf*>anwJbo`R8I;qI%`)jQKk zHG$vTn}i<>4Et#`pZ2sJ_sU7HI$xT=&!bL&*!9;9%?HtQp{u8kmtEmbKj6i9>y?E{ zS;dYV7s)j$f)W~SoTbp;2m1=!4En*!7d1yj*)g4#0-3GJWHD7Us+4OQRyED{ebxQ% zg5q9=h6+c(%?e{fNm$2?GEbpS(+;ONmJ*SRob(wM9^|_#iTZ~loQRaB1-sa3O~=z5 z9;vBOo&7bYFw$GHF(hbQ~8g z7w={*Qh2PY(>tD>ctdCVnoAwGdgOQ=Z^J5ba!qpoREv|5CNkm%Ic`2?$*OK+>gt+# zy<-#qv``Y8?^xwRO75Fy8@q7b@1yG>!S%DQoL2yGwC~U<($VGL@&^MK&N(KlD$*-v##(r_% zXpT+`nTxRAn;q`|Bry`~IeMCD9-So%U^Pr6mW`)%N}?U^<;gl|Dl>c`EA#d7*lIk?wWLv4EhlyX`aRR4nUQ3;g0_OJ}p8Za*95 zo+!SH@WA>0n4HzSJ?$*d0;tw97+b)uGJ3(WEbn?Yd`@1EAjL-Rn@zi{hC>UZMdaO? zEs4~pS)62ZsTinSZ;+j{Udy(S{unE!pFPx=)m-+X$Nv8KH#)D|sXB0$Lp7(mj+Ko; zqh7O-pl(c~2phL=F!e!fNWj4B3#>bTcDa$H`Ek zD|uLUQo60rnp{V$N0sJ++6{PTDDP0NtM6qLWS+uD71xjTrF?B`_Pq?YE#Any3-eiN z14Vxe?+=kF=y_DChCA>l4cOV(nBN6--YZk0Vz)nJ=5;<5Q;3PV7p8BBQoi&19gQC? zte8VN{G@yPKh+t~dlb%}@K^-(s<3x0G~2H;li2cYueUzpY859FN)bb!$b53lcbfl8!Z zY{K<7mmhz6N|4bp1bsgfKDZv(x9T)|>N4rIkWnjSD8(57k1^$(j;=Lz-w#T!M`*I| zK9;9VJ8ZV4^EheA)PA{xbfGczpcMQU;UyNG4 zt3iePnAMP{hbyeooypkBgl8V#hpT&1EIPqkCEHv^eYT+OLBOJ`8Wqx%)ef6GFL?Jg?OX`n)TR zotT|36~v}6M3@9|pZWENL_|@7@IG1wds0P`pgEI_+U76SK1tO`bxMR?XX_Ue)koH4 z5^Qh<)qJ>Gb^S TLqs4Pjv8tt1=b0IumSxW$w-nH^YZ&Y)3l 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≈ToREZ+w|REfj42A zMmU;BAIA3x!w#-9NN5HMUkz0FA!(sFj*yH0bEq&%r+7|Z#>>Wd=wd;JcN7hlPJ*6C z)%V9ua#1$+k4O(y@kl`($a#qH#gP(j=ITp=*Ax4O&A#2agMw;=+@W1`4Pppqx;6Sp zRH9!yP)Q`E89E7j;o%?@*HS&@*JxS77af8|?fY1QoI zDWdDxI{0#7;qm~W#Qee+!>J(HglRg^)OBN|pcNw3mN3QvU*i&BQV@DIQf13M912U^4cyt#Qh47`kY@Crs0ZV+Xb;fL-t(% znYKQ1ilV+Gay%Y)aoJ1-b{BAnp;5=pnDI0rql$TZN5^$UDNyCNZT^;JY(~=x{ijUl zImzHH)H)|;y&g7V4ocb3lydwbqJAGarTrD0wO?j_k55s*?l3NQ5c;rNuB|i5#y%?_;;^` zS2<_l>uqlYJhSio$J3~HeSBydGj6A45&a38t2lDDK6%8~IpLL6kYEWl8~yNKS++bh znX5c&4{UQ`hBGhI4<^584+zey^@kUbBdrm3RJ(Z!+Hl{ZmY0 z?3&fNH=}X4Q+7z|=e}F%hct)rwcq+Ia!IdpKiKhIdPhaSJ~PY;V5j`TG4Uuz=J>64 zS9U_O)gE%`t}gl}I>K0c`oxa~IuB`9M1RBAVcd>yz{n0}UuZgo?|2-WQu572@dvJ+ z{F_pg4P_T8We_E>xp&xOA1{3y1#49P>^)AR;}n3DN6K}|d%!evy3`%y2ES^@N{(pz zEOhD_rrDLdCVyyEF%Y%ik@bbpW^$gV`R`EWIr+at+(j_|z&2G~NYP%&V@@>4`|yZo z?@Y!Hyzi%los?EM*XS ze69J`M9w>$ZI)llL7+m_#Y6uhA43Dc$DwH?E(zfr1q{)n@iy#aJUy#%h zO-R!T;@PvX*We1Tqmai%EHH-$b{LUiaEqYMbg@l!eo4pUBVl*tpp;9ej&bnfAccv+T`Y)@pK)YVt_O?0Uc9p zYvx2L=Ub>$8ZKnR9&C#2eDDqX?^p=wh_S7_%a2=KT4kZ4>G&oJ1`r6?E@Z#Ryk*Bk zI^zx?icvpMtivU{I`7fC0Cg|7cSZF0cx^SB#Z17+!yE5(vaF#x^KO-b)K>I|+XMlO z`!`4SZCb^f$|EEt5Ff{fl(|~n*Pj`wW%buSpJruB1uOtIO5EP-Mrb~tl9Rw#hgyhI1Qj@~8b1=hx4RW`Xcgu|6G3l&|P%Y zK5LnafDO{X94SJ3bIHyEIXd&*)BOl@DyCr>*ya+r8^9|8e{`^~qpeMKzLtI=Ah)~X z^V^&#e0k6~5P7jO;}8Jyd%fBj{?vMNRVw^}R{!X6TXLzL(jPpOb+$k(!^ulh;lOOw z_I7~F-^yL#cB4(Rl|x@*oV$dKzlrC%WKOal)yCjc?@`_xK9v1p%R9s#8KIV&(iSPddQrLQK From 487cc2d84e8b1848f694ff6d99ca33ec4921f41f Mon Sep 17 00:00:00 2001 From: Steve Yurong Su Date: Sun, 15 May 2022 00:16:19 +0800 Subject: [PATCH 011/436] [IOTDB-3145] Support data type inferring in Expressions (#5900) Support data type inferring in Expressions Support comparing BOOLEAN and TEXT --- .../db/integration/IoTDBArithmeticIT.java | 2 +- .../db/mpp/plan/analyze/TypeProvider.java | 4 + .../iotdb/db/query/expression/Expression.java | 35 ++++++-- .../expression/binary/AdditionExpression.java | 2 +- .../binary/ArithmeticBinaryExpression.java | 60 +++++++++++++ .../binary/CompareBinaryExpression.java | 85 +++++++++++++++++++ .../expression/binary/DivisionExpression.java | 2 +- .../expression/binary/EqualToExpression.java | 2 +- .../binary/GreaterEqualExpression.java | 2 +- .../binary/GreaterThanExpression.java | 2 +- .../binary/LessEqualExpression.java | 2 +- .../expression/binary/LessThanExpression.java | 2 +- .../expression/binary/LogicAndExpression.java | 2 +- .../binary/LogicBinaryExpression.java | 50 +++++++++++ .../expression/binary/LogicOrExpression.java | 2 +- .../expression/binary/ModuloExpression.java | 2 +- .../binary/MultiplicationExpression.java | 2 +- .../expression/binary/NonEqualExpression.java | 2 +- .../binary/SubtractionExpression.java | 2 +- .../expression/leaf/ConstantOperand.java | 7 ++ .../expression/leaf/TimeSeriesOperand.java | 7 ++ .../expression/leaf/TimestampOperand.java | 7 ++ .../expression/multi/FunctionExpression.java | 33 +++++++ .../query/expression/unary/InExpression.java | 11 +++ .../expression/unary/LikeExpression.java | 14 +++ .../expression/unary/LogicNotExpression.java | 14 +++ .../expression/unary/NegationExpression.java | 20 +++++ .../expression/unary/RegularExpression.java | 14 +++ .../customizer/parameter/UDFParameters.java | 12 +++ .../udf/core/executor/UDTFTypeInferrer.java | 68 +++++++++++++++ .../binary/ArithmeticBinaryTransformer.java | 9 +- .../binary/CompareBinaryTransformer.java | 60 ++++++++++--- .../binary/CompareEqualToTransformer.java | 27 +++--- .../CompareGreaterEqualTransformer.java | 17 +++- .../binary/CompareGreaterThanTransformer.java | 17 +++- .../binary/CompareLessEqualTransformer.java | 17 +++- .../binary/CompareLessThanTransformer.java | 17 +++- .../binary/CompareNonEqualTransformer.java | 26 +++--- .../binary/LogicBinaryTransformer.java | 10 +-- 39 files changed, 587 insertions(+), 82 deletions(-) create mode 100644 server/src/main/java/org/apache/iotdb/db/query/expression/binary/ArithmeticBinaryExpression.java create mode 100644 server/src/main/java/org/apache/iotdb/db/query/expression/binary/CompareBinaryExpression.java create mode 100644 server/src/main/java/org/apache/iotdb/db/query/expression/binary/LogicBinaryExpression.java create mode 100644 server/src/main/java/org/apache/iotdb/db/query/udf/core/executor/UDTFTypeInferrer.java diff --git a/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBArithmeticIT.java b/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBArithmeticIT.java index 709494a978ab..f82d9ccb47c1 100644 --- a/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBArithmeticIT.java +++ b/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBArithmeticIT.java @@ -281,7 +281,7 @@ public void testWrongTypeText() { Statement statement = connection.createStatement()) { statement.executeQuery("select s1 + s6 from root.sg.d1"); } catch (SQLException throwable) { - assertTrue(throwable.getMessage().contains("Unsupported data type: TEXT")); + assertTrue(throwable.getMessage().contains("Unsupported dataType: TEXT")); } } } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/TypeProvider.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/TypeProvider.java index 34d8acfc9a7c..6c70c44e2d1b 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/TypeProvider.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/TypeProvider.java @@ -55,6 +55,10 @@ public void setType(String path, TSDataType dataType) { this.typeMap.put(path, dataType); } + public boolean containsTypeInfoOf(String path) { + return typeMap.containsKey(path); + } + public void serialize(ByteBuffer byteBuffer) { ReadWriteIOUtils.write(typeMap.size(), byteBuffer); for (Map.Entry entry : typeMap.entrySet()) { diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/Expression.java b/server/src/main/java/org/apache/iotdb/db/query/expression/Expression.java index 14d5007739c6..4dceb6106ea6 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/Expression.java +++ b/server/src/main/java/org/apache/iotdb/db/query/expression/Expression.java @@ -22,6 +22,8 @@ import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.exception.query.LogicalOptimizeException; import org.apache.iotdb.db.exception.query.QueryProcessException; +import org.apache.iotdb.db.exception.sql.SemanticException; +import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; import org.apache.iotdb.db.qp.physical.crud.UDTFPlan; import org.apache.iotdb.db.query.expression.binary.AdditionExpression; import org.apache.iotdb.db.query.expression.binary.DivisionExpression; @@ -56,6 +58,7 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.time.ZoneId; +import java.util.Arrays; import java.util.Deque; import java.util.Iterator; import java.util.LinkedList; @@ -108,6 +111,24 @@ public abstract void removeWildcards( public abstract void constructUdfExecutors( Map expressionName2Executor, ZoneId zoneId); + ///////////////////////////////////////////////////////////////////////////////////////////////// + // type inference + ///////////////////////////////////////////////////////////////////////////////////////////////// + public abstract TSDataType inferTypes(TypeProvider typeProvider); + + protected static void checkInputExpressionDataType( + String expressionString, TSDataType actual, TSDataType... expected) { + for (TSDataType type : expected) { + if (actual.equals(type)) { + return; + } + } + throw new SemanticException( + String.format( + "Invalid input expression data type. expression: %s, actual data type: %s, expected data type(s): %s.", + expressionString, actual.name(), Arrays.toString(expected))); + } + ///////////////////////////////////////////////////////////////////////////////////////////////// // For expression evaluation DAG building ///////////////////////////////////////////////////////////////////////////////////////////////// @@ -127,15 +148,6 @@ public abstract IntermediateLayer constructIntermediateLayer( LayerMemoryAssigner memoryAssigner) throws QueryProcessException, IOException; - ///////////////////////////////////////////////////////////////////////////////////////////////// - // getExpressions: returning DIRECT children expressions - ///////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * returns the DIRECT children expressions if it has any, otherwise an EMPTY list will be returned - */ - public abstract List getExpressions(); - ///////////////////////////////////////////////////////////////////////////////////////////////// // isConstantOperand ///////////////////////////////////////////////////////////////////////////////////////////////// @@ -246,6 +258,11 @@ public Expression next() { } } + /** + * returns the DIRECT children expressions if it has any, otherwise an EMPTY list will be returned + */ + public abstract List getExpressions(); + ///////////////////////////////////////////////////////////////////////////////////////////////// // serialize & deserialize ///////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/AdditionExpression.java b/server/src/main/java/org/apache/iotdb/db/query/expression/binary/AdditionExpression.java index 6ec5ce839922..6fd5e698fc8d 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/AdditionExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/query/expression/binary/AdditionExpression.java @@ -27,7 +27,7 @@ import java.nio.ByteBuffer; -public class AdditionExpression extends BinaryExpression { +public class AdditionExpression extends ArithmeticBinaryExpression { public AdditionExpression(Expression leftExpression, Expression rightExpression) { super(leftExpression, rightExpression); diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/ArithmeticBinaryExpression.java b/server/src/main/java/org/apache/iotdb/db/query/expression/binary/ArithmeticBinaryExpression.java new file mode 100644 index 000000000000..185a8ce617de --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/query/expression/binary/ArithmeticBinaryExpression.java @@ -0,0 +1,60 @@ +/* + * 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.iotdb.db.query.expression.binary; + +import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; +import org.apache.iotdb.db.query.expression.Expression; +import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; + +import java.nio.ByteBuffer; + +public abstract class ArithmeticBinaryExpression extends BinaryExpression { + + protected ArithmeticBinaryExpression(Expression leftExpression, Expression rightExpression) { + super(leftExpression, rightExpression); + } + + protected ArithmeticBinaryExpression(ByteBuffer byteBuffer) { + super(byteBuffer); + } + + @Override + public final TSDataType inferTypes(TypeProvider typeProvider) { + final String expressionString = toString(); + if (!typeProvider.containsTypeInfoOf(expressionString)) { + checkInputExpressionDataType( + leftExpression.toString(), + leftExpression.inferTypes(typeProvider), + TSDataType.INT32, + TSDataType.INT64, + TSDataType.FLOAT, + TSDataType.DOUBLE); + checkInputExpressionDataType( + rightExpression.toString(), + rightExpression.inferTypes(typeProvider), + TSDataType.INT32, + TSDataType.INT64, + TSDataType.FLOAT, + TSDataType.DOUBLE); + typeProvider.setType(expressionString, TSDataType.DOUBLE); + } + return TSDataType.DOUBLE; + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/CompareBinaryExpression.java b/server/src/main/java/org/apache/iotdb/db/query/expression/binary/CompareBinaryExpression.java new file mode 100644 index 000000000000..66f795faa1c0 --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/query/expression/binary/CompareBinaryExpression.java @@ -0,0 +1,85 @@ +/* + * 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.iotdb.db.query.expression.binary; + +import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; +import org.apache.iotdb.db.query.expression.Expression; +import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; + +import java.nio.ByteBuffer; + +public abstract class CompareBinaryExpression extends BinaryExpression { + + protected CompareBinaryExpression(Expression leftExpression, Expression rightExpression) { + super(leftExpression, rightExpression); + } + + protected CompareBinaryExpression(ByteBuffer byteBuffer) { + super(byteBuffer); + } + + @Override + public TSDataType inferTypes(TypeProvider typeProvider) { + final String expressionString = toString(); + + if (!typeProvider.containsTypeInfoOf(expressionString)) { + final TSDataType leftExpressionDataType = leftExpression.inferTypes(typeProvider); + final TSDataType rightExpressionDataType = rightExpression.inferTypes(typeProvider); + + if (!leftExpressionDataType.equals(rightExpressionDataType)) { + final String leftExpressionString = leftExpression.toString(); + final String rightExpressionString = rightExpression.toString(); + + if (TSDataType.BOOLEAN.equals(leftExpressionDataType) + || TSDataType.BOOLEAN.equals(rightExpressionDataType)) { + checkInputExpressionDataType( + leftExpressionString, leftExpressionDataType, TSDataType.BOOLEAN); + checkInputExpressionDataType( + rightExpressionString, rightExpressionDataType, TSDataType.BOOLEAN); + } else if (TSDataType.TEXT.equals(leftExpressionDataType) + || TSDataType.TEXT.equals(rightExpressionDataType)) { + checkInputExpressionDataType( + leftExpressionString, leftExpressionDataType, TSDataType.TEXT); + checkInputExpressionDataType( + rightExpressionString, rightExpressionDataType, TSDataType.TEXT); + } else { + checkInputExpressionDataType( + leftExpressionString, + leftExpressionDataType, + TSDataType.INT32, + TSDataType.INT64, + TSDataType.FLOAT, + TSDataType.DOUBLE); + checkInputExpressionDataType( + rightExpressionString, + rightExpressionDataType, + TSDataType.INT32, + TSDataType.INT64, + TSDataType.FLOAT, + TSDataType.DOUBLE); + } + } + + typeProvider.setType(expressionString, TSDataType.BOOLEAN); + } + + return TSDataType.BOOLEAN; + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/DivisionExpression.java b/server/src/main/java/org/apache/iotdb/db/query/expression/binary/DivisionExpression.java index 5ba106f459a6..8eb9308d9148 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/DivisionExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/query/expression/binary/DivisionExpression.java @@ -27,7 +27,7 @@ import java.nio.ByteBuffer; -public class DivisionExpression extends BinaryExpression { +public class DivisionExpression extends ArithmeticBinaryExpression { public DivisionExpression(Expression leftExpression, Expression rightExpression) { super(leftExpression, rightExpression); diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/EqualToExpression.java b/server/src/main/java/org/apache/iotdb/db/query/expression/binary/EqualToExpression.java index aa85becd0797..d9662844023d 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/EqualToExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/query/expression/binary/EqualToExpression.java @@ -27,7 +27,7 @@ import java.nio.ByteBuffer; -public class EqualToExpression extends BinaryExpression { +public class EqualToExpression extends CompareBinaryExpression { public EqualToExpression(Expression leftExpression, Expression rightExpression) { super(leftExpression, rightExpression); diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/GreaterEqualExpression.java b/server/src/main/java/org/apache/iotdb/db/query/expression/binary/GreaterEqualExpression.java index 0364212ac8f0..6d06a29284e7 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/GreaterEqualExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/query/expression/binary/GreaterEqualExpression.java @@ -27,7 +27,7 @@ import java.nio.ByteBuffer; -public class GreaterEqualExpression extends BinaryExpression { +public class GreaterEqualExpression extends CompareBinaryExpression { public GreaterEqualExpression(Expression leftExpression, Expression rightExpression) { super(leftExpression, rightExpression); diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/GreaterThanExpression.java b/server/src/main/java/org/apache/iotdb/db/query/expression/binary/GreaterThanExpression.java index 56589b5e1046..6a574f68e98a 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/GreaterThanExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/query/expression/binary/GreaterThanExpression.java @@ -27,7 +27,7 @@ import java.nio.ByteBuffer; -public class GreaterThanExpression extends BinaryExpression { +public class GreaterThanExpression extends CompareBinaryExpression { public GreaterThanExpression(Expression leftExpression, Expression rightExpression) { super(leftExpression, rightExpression); diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/LessEqualExpression.java b/server/src/main/java/org/apache/iotdb/db/query/expression/binary/LessEqualExpression.java index 599a05f67b61..a0fff83b49ee 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/LessEqualExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/query/expression/binary/LessEqualExpression.java @@ -27,7 +27,7 @@ import java.nio.ByteBuffer; -public class LessEqualExpression extends BinaryExpression { +public class LessEqualExpression extends CompareBinaryExpression { public LessEqualExpression(Expression leftExpression, Expression rightExpression) { super(leftExpression, rightExpression); diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/LessThanExpression.java b/server/src/main/java/org/apache/iotdb/db/query/expression/binary/LessThanExpression.java index 1852771e8e9f..8f4978039248 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/LessThanExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/query/expression/binary/LessThanExpression.java @@ -27,7 +27,7 @@ import java.nio.ByteBuffer; -public class LessThanExpression extends BinaryExpression { +public class LessThanExpression extends CompareBinaryExpression { public LessThanExpression(Expression leftExpression, Expression rightExpression) { super(leftExpression, rightExpression); diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/LogicAndExpression.java b/server/src/main/java/org/apache/iotdb/db/query/expression/binary/LogicAndExpression.java index 35f19c258b5a..26ac76b3c240 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/LogicAndExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/query/expression/binary/LogicAndExpression.java @@ -27,7 +27,7 @@ import java.nio.ByteBuffer; -public class LogicAndExpression extends BinaryExpression { +public class LogicAndExpression extends LogicBinaryExpression { public LogicAndExpression(Expression leftExpression, Expression rightExpression) { super(leftExpression, rightExpression); diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/LogicBinaryExpression.java b/server/src/main/java/org/apache/iotdb/db/query/expression/binary/LogicBinaryExpression.java new file mode 100644 index 000000000000..9ad7e20bfa2d --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/query/expression/binary/LogicBinaryExpression.java @@ -0,0 +1,50 @@ +/* + * 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.iotdb.db.query.expression.binary; + +import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; +import org.apache.iotdb.db.query.expression.Expression; +import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; + +import java.nio.ByteBuffer; + +public abstract class LogicBinaryExpression extends BinaryExpression { + + protected LogicBinaryExpression(Expression leftExpression, Expression rightExpression) { + super(leftExpression, rightExpression); + } + + protected LogicBinaryExpression(ByteBuffer byteBuffer) { + super(byteBuffer); + } + + @Override + public final TSDataType inferTypes(TypeProvider typeProvider) { + final String expressionString = toString(); + if (!typeProvider.containsTypeInfoOf(expressionString)) { + checkInputExpressionDataType( + leftExpression.toString(), leftExpression.inferTypes(typeProvider), TSDataType.BOOLEAN); + checkInputExpressionDataType( + rightExpression.toString(), rightExpression.inferTypes(typeProvider), TSDataType.BOOLEAN); + typeProvider.setType(expressionString, TSDataType.BOOLEAN); + } + return TSDataType.BOOLEAN; + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/LogicOrExpression.java b/server/src/main/java/org/apache/iotdb/db/query/expression/binary/LogicOrExpression.java index ee0609ae1642..eff1d563578b 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/LogicOrExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/query/expression/binary/LogicOrExpression.java @@ -27,7 +27,7 @@ import java.nio.ByteBuffer; -public class LogicOrExpression extends BinaryExpression { +public class LogicOrExpression extends LogicBinaryExpression { public LogicOrExpression(Expression leftExpression, Expression rightExpression) { super(leftExpression, rightExpression); diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/ModuloExpression.java b/server/src/main/java/org/apache/iotdb/db/query/expression/binary/ModuloExpression.java index e43f23f3c59c..39f4738acac3 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/ModuloExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/query/expression/binary/ModuloExpression.java @@ -27,7 +27,7 @@ import java.nio.ByteBuffer; -public class ModuloExpression extends BinaryExpression { +public class ModuloExpression extends ArithmeticBinaryExpression { public ModuloExpression(Expression leftExpression, Expression rightExpression) { super(leftExpression, rightExpression); diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/MultiplicationExpression.java b/server/src/main/java/org/apache/iotdb/db/query/expression/binary/MultiplicationExpression.java index d5b15dc55d94..6b72d5a2d275 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/MultiplicationExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/query/expression/binary/MultiplicationExpression.java @@ -27,7 +27,7 @@ import java.nio.ByteBuffer; -public class MultiplicationExpression extends BinaryExpression { +public class MultiplicationExpression extends ArithmeticBinaryExpression { public MultiplicationExpression(Expression leftExpression, Expression rightExpression) { super(leftExpression, rightExpression); diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/NonEqualExpression.java b/server/src/main/java/org/apache/iotdb/db/query/expression/binary/NonEqualExpression.java index 6c9337fa57c4..542d4a7c2559 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/NonEqualExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/query/expression/binary/NonEqualExpression.java @@ -27,7 +27,7 @@ import java.nio.ByteBuffer; -public class NonEqualExpression extends BinaryExpression { +public class NonEqualExpression extends CompareBinaryExpression { public NonEqualExpression(Expression leftExpression, Expression rightExpression) { super(leftExpression, rightExpression); diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/SubtractionExpression.java b/server/src/main/java/org/apache/iotdb/db/query/expression/binary/SubtractionExpression.java index 3e31ad903960..372d56cc73a0 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/SubtractionExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/query/expression/binary/SubtractionExpression.java @@ -27,7 +27,7 @@ import java.nio.ByteBuffer; -public class SubtractionExpression extends BinaryExpression { +public class SubtractionExpression extends ArithmeticBinaryExpression { public SubtractionExpression(Expression leftExpression, Expression rightExpression) { super(leftExpression, rightExpression); diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/leaf/ConstantOperand.java b/server/src/main/java/org/apache/iotdb/db/query/expression/leaf/ConstantOperand.java index d92c8021c333..7acd877fc08e 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/leaf/ConstantOperand.java +++ b/server/src/main/java/org/apache/iotdb/db/query/expression/leaf/ConstantOperand.java @@ -21,6 +21,7 @@ import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.exception.query.QueryProcessException; +import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; import org.apache.iotdb.db.qp.physical.crud.UDTFPlan; import org.apache.iotdb.db.query.expression.Expression; import org.apache.iotdb.db.query.expression.ExpressionType; @@ -91,6 +92,12 @@ public void collectPaths(Set pathSet) { // Do nothing } + @Override + public TSDataType inferTypes(TypeProvider typeProvider) { + typeProvider.setType(toString(), dataType); + return dataType; + } + @Override public void bindInputLayerColumnIndexWithExpression(UDTFPlan udtfPlan) { // Do nothing diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/leaf/TimeSeriesOperand.java b/server/src/main/java/org/apache/iotdb/db/query/expression/leaf/TimeSeriesOperand.java index 11a6b72912f0..5d896a932f25 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/leaf/TimeSeriesOperand.java +++ b/server/src/main/java/org/apache/iotdb/db/query/expression/leaf/TimeSeriesOperand.java @@ -23,6 +23,7 @@ import org.apache.iotdb.db.exception.query.LogicalOptimizeException; import org.apache.iotdb.db.exception.query.QueryProcessException; import org.apache.iotdb.db.metadata.path.PathDeserializeUtil; +import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; import org.apache.iotdb.db.qp.physical.crud.UDTFPlan; import org.apache.iotdb.db.query.expression.Expression; import org.apache.iotdb.db.query.expression.ExpressionType; @@ -87,6 +88,11 @@ public void collectPaths(Set pathSet) { pathSet.add(path); } + @Override + public TSDataType inferTypes(TypeProvider typeProvider) { + return typeProvider.getType(toString()); + } + @Override public void bindInputLayerColumnIndexWithExpression(UDTFPlan udtfPlan) { inputColumnIndex = udtfPlan.getReaderIndexByExpressionName(toString()); @@ -125,6 +131,7 @@ public IntermediateLayer constructIntermediateLayer( return expressionIntermediateLayerMap.get(this); } + @Override public String getExpressionStringInternal() { return path.isMeasurementAliasExists() ? path.getFullPathWithAlias() : path.getFullPath(); } diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/leaf/TimestampOperand.java b/server/src/main/java/org/apache/iotdb/db/query/expression/leaf/TimestampOperand.java index 73c77a2b3716..bb473451b753 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/leaf/TimestampOperand.java +++ b/server/src/main/java/org/apache/iotdb/db/query/expression/leaf/TimestampOperand.java @@ -22,6 +22,7 @@ import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.exception.query.LogicalOptimizeException; import org.apache.iotdb.db.exception.query.QueryProcessException; +import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; import org.apache.iotdb.db.qp.physical.crud.UDTFPlan; import org.apache.iotdb.db.query.expression.Expression; import org.apache.iotdb.db.query.expression.ExpressionType; @@ -75,6 +76,12 @@ public void collectPaths(Set pathSet) { pathSet.add(TIMESTAMP_PARTIAL_PATH); } + @Override + public TSDataType inferTypes(TypeProvider typeProvider) { + typeProvider.setType(toString(), TSDataType.INT64); + return TSDataType.INT64; + } + @Override public void bindInputLayerColumnIndexWithExpression(UDTFPlan udtfPlan) { // do nothing diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/multi/FunctionExpression.java b/server/src/main/java/org/apache/iotdb/db/query/expression/multi/FunctionExpression.java index b2bd93b05c90..0ec226403f03 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/multi/FunctionExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/query/expression/multi/FunctionExpression.java @@ -23,6 +23,8 @@ import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.exception.query.LogicalOptimizeException; import org.apache.iotdb.db.exception.query.QueryProcessException; +import org.apache.iotdb.db.exception.sql.SemanticException; +import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; import org.apache.iotdb.db.qp.constant.SQLConstant; import org.apache.iotdb.db.qp.physical.crud.UDTFPlan; import org.apache.iotdb.db.qp.strategy.optimizer.ConcatPathOptimizer; @@ -32,6 +34,7 @@ import org.apache.iotdb.db.query.udf.api.customizer.strategy.AccessStrategy; import org.apache.iotdb.db.query.udf.core.executor.UDTFContext; import org.apache.iotdb.db.query.udf.core.executor.UDTFExecutor; +import org.apache.iotdb.db.query.udf.core.executor.UDTFTypeInferrer; import org.apache.iotdb.db.query.udf.core.layer.IntermediateLayer; import org.apache.iotdb.db.query.udf.core.layer.LayerMemoryAssigner; import org.apache.iotdb.db.query.udf.core.layer.MultiInputColumnIntermediateLayer; @@ -43,6 +46,7 @@ import org.apache.iotdb.db.query.udf.core.transformer.multi.UDFQueryRowWindowTransformer; import org.apache.iotdb.db.query.udf.core.transformer.multi.UDFQueryTransformer; import org.apache.iotdb.db.query.udf.core.transformer.unary.TransparentTransformer; +import org.apache.iotdb.db.utils.TypeInferenceUtils; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils; @@ -238,6 +242,35 @@ public void constructUdfExecutors( expressionName2Executor.put(expressionString, new UDTFExecutor(this, zoneId)); } + @Override + public TSDataType inferTypes(TypeProvider typeProvider) { + final String expressionString = toString(); + + if (!typeProvider.containsTypeInfoOf(expressionString)) { + for (Expression expression : expressions) { + expression.inferTypes(typeProvider); + } + + if (isTimeSeriesGeneratingFunctionExpression()) { + typeProvider.setType( + expressionString, new UDTFTypeInferrer(this).inferOutputType(typeProvider)); + } else { + if (expressions.size() != 1) { + throw new SemanticException( + String.format( + "Builtin aggregation function only accepts 1 input expression. Actual %d input expressions.", + expressions.size())); + } + typeProvider.setType( + expressionString, + TypeInferenceUtils.getAggrDataType( + functionName, typeProvider.getType(expressions.get(0).toString()))); + } + } + + return typeProvider.getType(expressionString); + } + @Override public void bindInputLayerColumnIndexWithExpression(UDTFPlan udtfPlan) { for (Expression expression : expressions) { diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/unary/InExpression.java b/server/src/main/java/org/apache/iotdb/db/query/expression/unary/InExpression.java index 9f329b37281a..979d4c2bbc91 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/unary/InExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/query/expression/unary/InExpression.java @@ -19,11 +19,13 @@ package org.apache.iotdb.db.query.expression.unary; +import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; import org.apache.iotdb.db.query.expression.Expression; import org.apache.iotdb.db.query.expression.ExpressionType; import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; import org.apache.iotdb.db.query.udf.core.transformer.Transformer; import org.apache.iotdb.db.query.udf.core.transformer.unary.InTransformer; +import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils; import java.nio.ByteBuffer; @@ -59,6 +61,15 @@ public LinkedHashSet getValues() { return values; } + @Override + public TSDataType inferTypes(TypeProvider typeProvider) { + final String expressionString = toString(); + if (!typeProvider.containsTypeInfoOf(expressionString)) { + typeProvider.setType(expressionString, expression.inferTypes(typeProvider)); + } + return typeProvider.getType(expressionString); + } + @Override protected String getExpressionStringInternal() { StringBuilder valuesStringBuilder = new StringBuilder(); diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/unary/LikeExpression.java b/server/src/main/java/org/apache/iotdb/db/query/expression/unary/LikeExpression.java index 2d91ac072021..0e94037e721c 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/unary/LikeExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/query/expression/unary/LikeExpression.java @@ -19,11 +19,14 @@ package org.apache.iotdb.db.query.expression.unary; +import org.apache.iotdb.db.exception.sql.SemanticException; +import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; import org.apache.iotdb.db.query.expression.Expression; import org.apache.iotdb.db.query.expression.ExpressionType; import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; import org.apache.iotdb.db.query.udf.core.transformer.Transformer; import org.apache.iotdb.db.query.udf.core.transformer.unary.RegularTransformer; +import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils; import java.nio.ByteBuffer; @@ -115,6 +118,17 @@ private String unescapeString(String value) { return stringBuilder.toString(); } + @Override + public TSDataType inferTypes(TypeProvider typeProvider) throws SemanticException { + final String expressionString = toString(); + if (!typeProvider.containsTypeInfoOf(expressionString)) { + checkInputExpressionDataType( + expression.toString(), expression.inferTypes(typeProvider), TSDataType.TEXT); + typeProvider.setType(expressionString, TSDataType.TEXT); + } + return TSDataType.TEXT; + } + @Override protected String getExpressionStringInternal() { return expression + " LIKE '" + pattern + "'"; diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/unary/LogicNotExpression.java b/server/src/main/java/org/apache/iotdb/db/query/expression/unary/LogicNotExpression.java index 2c22a5284134..a538e4a02833 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/unary/LogicNotExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/query/expression/unary/LogicNotExpression.java @@ -19,6 +19,8 @@ package org.apache.iotdb.db.query.expression.unary; +import org.apache.iotdb.db.exception.sql.SemanticException; +import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; import org.apache.iotdb.db.query.expression.Expression; import org.apache.iotdb.db.query.expression.ExpressionType; import org.apache.iotdb.db.query.expression.leaf.ConstantOperand; @@ -27,6 +29,7 @@ import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; import org.apache.iotdb.db.query.udf.core.transformer.Transformer; import org.apache.iotdb.db.query.udf.core.transformer.unary.LogicNotTransformer; +import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.nio.ByteBuffer; @@ -50,6 +53,17 @@ protected Expression constructExpression(Expression childExpression) { return new LogicNotExpression(childExpression); } + @Override + public TSDataType inferTypes(TypeProvider typeProvider) throws SemanticException { + final String expressionString = toString(); + if (!typeProvider.containsTypeInfoOf(expressionString)) { + checkInputExpressionDataType( + expression.toString(), expression.inferTypes(typeProvider), TSDataType.BOOLEAN); + typeProvider.setType(expressionString, TSDataType.BOOLEAN); + } + return TSDataType.BOOLEAN; + } + @Override public String getExpressionStringInternal() { return expression instanceof FunctionExpression diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/unary/NegationExpression.java b/server/src/main/java/org/apache/iotdb/db/query/expression/unary/NegationExpression.java index 8e12a85a57a8..6d8c11107821 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/unary/NegationExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/query/expression/unary/NegationExpression.java @@ -19,6 +19,8 @@ package org.apache.iotdb.db.query.expression.unary; +import org.apache.iotdb.db.exception.sql.SemanticException; +import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; import org.apache.iotdb.db.query.expression.Expression; import org.apache.iotdb.db.query.expression.ExpressionType; import org.apache.iotdb.db.query.expression.leaf.ConstantOperand; @@ -27,6 +29,7 @@ import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; import org.apache.iotdb.db.query.udf.core.transformer.Transformer; import org.apache.iotdb.db.query.udf.core.transformer.unary.ArithmeticNegationTransformer; +import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.nio.ByteBuffer; @@ -50,6 +53,23 @@ protected Expression constructExpression(Expression childExpression) { return new NegationExpression(childExpression); } + @Override + public TSDataType inferTypes(TypeProvider typeProvider) throws SemanticException { + final String expressionString = toString(); + if (!typeProvider.containsTypeInfoOf(expressionString)) { + TSDataType inputExpressionType = expression.inferTypes(typeProvider); + checkInputExpressionDataType( + expression.toString(), + inputExpressionType, + TSDataType.INT32, + TSDataType.INT64, + TSDataType.FLOAT, + TSDataType.DOUBLE); + typeProvider.setType(expressionString, inputExpressionType); + } + return typeProvider.getType(expressionString); + } + @Override public String getExpressionStringInternal() { return expression instanceof TimeSeriesOperand diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/unary/RegularExpression.java b/server/src/main/java/org/apache/iotdb/db/query/expression/unary/RegularExpression.java index f6027a9c8e1b..a8b30f0d5af1 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/unary/RegularExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/query/expression/unary/RegularExpression.java @@ -19,11 +19,14 @@ package org.apache.iotdb.db.query.expression.unary; +import org.apache.iotdb.db.exception.sql.SemanticException; +import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; import org.apache.iotdb.db.query.expression.Expression; import org.apache.iotdb.db.query.expression.ExpressionType; import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; import org.apache.iotdb.db.query.udf.core.transformer.Transformer; import org.apache.iotdb.db.query.udf.core.transformer.unary.RegularTransformer; +import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils; import org.apache.commons.lang3.Validate; @@ -72,6 +75,17 @@ protected Expression constructExpression(Expression childExpression) { return new RegularExpression(childExpression, patternString, pattern); } + @Override + public TSDataType inferTypes(TypeProvider typeProvider) throws SemanticException { + final String expressionString = toString(); + if (!typeProvider.containsTypeInfoOf(expressionString)) { + checkInputExpressionDataType( + expression.toString(), expression.inferTypes(typeProvider), TSDataType.TEXT); + typeProvider.setType(expressionString, TSDataType.TEXT); + } + return TSDataType.TEXT; + } + @Override protected String getExpressionStringInternal() { return expression + " REGEXP '" + patternString + "'"; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/api/customizer/parameter/UDFParameters.java b/server/src/main/java/org/apache/iotdb/db/query/udf/api/customizer/parameter/UDFParameters.java index 5f057ccf8d01..9b9b6640f096 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/api/customizer/parameter/UDFParameters.java +++ b/server/src/main/java/org/apache/iotdb/db/query/udf/api/customizer/parameter/UDFParameters.java @@ -22,6 +22,7 @@ import org.apache.iotdb.commons.exception.MetadataException; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.exception.query.QueryProcessException; +import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; import org.apache.iotdb.db.query.expression.Expression; import org.apache.iotdb.db.query.expression.multi.FunctionExpression; import org.apache.iotdb.db.query.udf.api.UDTF; @@ -63,6 +64,17 @@ public UDFParameters( } } + public UDFParameters(FunctionExpression functionExpression, TypeProvider typeProvider) + throws QueryProcessException { + expressions = functionExpression.getExpressions(); + paths = functionExpression.getPaths(); + attributes = functionExpression.getFunctionAttributes(); + dataTypes = new ArrayList<>(); + for (Expression expression : expressions) { + dataTypes.add(typeProvider.getType(expression.toString())); + } + } + public List getExpressions() { return expressions; } diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/executor/UDTFTypeInferrer.java b/server/src/main/java/org/apache/iotdb/db/query/udf/core/executor/UDTFTypeInferrer.java new file mode 100644 index 000000000000..ca7233906a54 --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/query/udf/core/executor/UDTFTypeInferrer.java @@ -0,0 +1,68 @@ +/* + * 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.iotdb.db.query.udf.core.executor; + +import org.apache.iotdb.db.exception.sql.SemanticException; +import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; +import org.apache.iotdb.db.query.expression.multi.FunctionExpression; +import org.apache.iotdb.db.query.udf.api.UDTF; +import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.db.query.udf.service.UDFRegistrationService; +import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.time.ZoneId; + +public class UDTFTypeInferrer { + + private static final Logger LOGGER = LoggerFactory.getLogger(UDTFTypeInferrer.class); + + protected final FunctionExpression expression; + + public UDTFTypeInferrer(FunctionExpression expression) { + this.expression = expression; + } + + public TSDataType inferOutputType(TypeProvider typeProvider) { + try { + UDTF udtf = (UDTF) UDFRegistrationService.getInstance().reflect(expression); + + UDFParameters parameters = new UDFParameters(expression, typeProvider); + udtf.validate(new UDFParameterValidator(parameters)); + + // use ZoneId.systemDefault() because UDF's data type is ZoneId independent + UDTFConfigurations configurations = new UDTFConfigurations(ZoneId.systemDefault()); + udtf.beforeStart(parameters, configurations); + + udtf.beforeDestroy(); + + return configurations.getOutputDataType(); + } catch (Exception e) { + LOGGER.warn("Error occurred during inferring UDF data type", e); + throw new SemanticException( + String.format("Error occurred during inferring UDF data type: %s", System.lineSeparator()) + + e); + } + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/ArithmeticBinaryTransformer.java b/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/ArithmeticBinaryTransformer.java index 29a17a1ee184..751fc534d62f 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/ArithmeticBinaryTransformer.java +++ b/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/ArithmeticBinaryTransformer.java @@ -35,11 +35,12 @@ protected ArithmeticBinaryTransformer( @Override protected void checkType() { - if (leftPointReaderDataType == TSDataType.BOOLEAN) { - throw new UnSupportedDataTypeException(leftPointReader.getDataType().toString()); + if (leftPointReaderDataType == TSDataType.BOOLEAN + || rightPointReaderDataType == TSDataType.BOOLEAN) { + throw new UnSupportedDataTypeException(TSDataType.BOOLEAN.name()); } - if (rightPointReaderDataType == TSDataType.BOOLEAN) { - throw new UnSupportedDataTypeException(rightPointReader.getDataType().toString()); + if (leftPointReaderDataType == TSDataType.TEXT || rightPointReaderDataType == TSDataType.TEXT) { + throw new UnSupportedDataTypeException(TSDataType.TEXT.name()); } } diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareBinaryTransformer.java b/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareBinaryTransformer.java index 90f506ddd7b7..42b42e105837 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareBinaryTransformer.java +++ b/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareBinaryTransformer.java @@ -25,35 +25,73 @@ import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.io.IOException; +import java.util.Objects; public abstract class CompareBinaryTransformer extends BinaryTransformer { + @FunctionalInterface + protected interface Evaluator { + + boolean evaluate() throws QueryProcessException, IOException; + } + + protected final Evaluator evaluator; + protected CompareBinaryTransformer( LayerPointReader leftPointReader, LayerPointReader rightPointReader) throws UnSupportedDataTypeException { super(leftPointReader, rightPointReader); + evaluator = + TSDataType.TEXT.equals(leftPointReaderDataType) + ? constructTextEvaluator() + : constructNumberEvaluator(); + } + + protected abstract Evaluator constructNumberEvaluator(); + + protected abstract Evaluator constructTextEvaluator(); + + protected static int compare(CharSequence cs1, CharSequence cs2) { + if (Objects.requireNonNull(cs1) == Objects.requireNonNull(cs2)) { + return 0; + } + + if (cs1.getClass() == cs2.getClass() && cs1 instanceof Comparable) { + return ((Comparable) cs1).compareTo(cs2); + } + + for (int i = 0, len = Math.min(cs1.length(), cs2.length()); i < len; i++) { + char a = cs1.charAt(i); + char b = cs2.charAt(i); + if (a != b) { + return a - b; + } + } + + return cs1.length() - cs2.length(); } @Override - protected void checkType() { - if (leftPointReaderDataType == TSDataType.BOOLEAN) { - throw new UnSupportedDataTypeException(leftPointReader.getDataType().toString()); + protected final void checkType() { + if (leftPointReaderDataType.equals(rightPointReaderDataType)) { + return; + } + + if (leftPointReaderDataType.equals(TSDataType.BOOLEAN) + || rightPointReaderDataType.equals(TSDataType.BOOLEAN)) { + throw new UnSupportedDataTypeException(TSDataType.BOOLEAN.toString()); } - if (rightPointReaderDataType == TSDataType.BOOLEAN) { - throw new UnSupportedDataTypeException(rightPointReader.getDataType().toString()); + if (leftPointReaderDataType.equals(TSDataType.TEXT) + || rightPointReaderDataType.equals(TSDataType.TEXT)) { + throw new UnSupportedDataTypeException(TSDataType.TEXT.toString()); } } @Override protected final void transformAndCache() throws QueryProcessException, IOException { - cachedBoolean = - evaluate( - castCurrentValueToDoubleOperand(leftPointReader, leftPointReaderDataType), - castCurrentValueToDoubleOperand(rightPointReader, rightPointReaderDataType)); + cachedBoolean = evaluator.evaluate(); } - abstract boolean evaluate(double leftOperand, double rightOperand); - @Override public TSDataType getDataType() { return TSDataType.BOOLEAN; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareEqualToTransformer.java b/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareEqualToTransformer.java index 8484df276c03..af6b2c8d651b 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareEqualToTransformer.java +++ b/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareEqualToTransformer.java @@ -20,8 +20,6 @@ package org.apache.iotdb.db.query.udf.core.transformer.binary; import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; -import org.apache.iotdb.tsfile.exception.write.UnSupportedDataTypeException; -import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; public class CompareEqualToTransformer extends CompareBinaryTransformer { @@ -31,21 +29,20 @@ public CompareEqualToTransformer( } @Override - protected void checkType() { - if ((leftPointReaderDataType == TSDataType.BOOLEAN - && rightPointReaderDataType != TSDataType.BOOLEAN) - || (leftPointReaderDataType != TSDataType.BOOLEAN - && rightPointReaderDataType == TSDataType.BOOLEAN)) { - throw new UnSupportedDataTypeException( - "left: " - + leftPointReaderDataType.toString() - + ", right: " - + rightPointReaderDataType.toString()); - } + protected Evaluator constructNumberEvaluator() { + return () -> + Double.compare( + castCurrentValueToDoubleOperand(leftPointReader, leftPointReaderDataType), + castCurrentValueToDoubleOperand(rightPointReader, rightPointReaderDataType)) + == 0; } @Override - protected boolean evaluate(double leftOperand, double rightOperand) { - return Double.compare(leftOperand, rightOperand) == 0; + protected Evaluator constructTextEvaluator() { + return () -> + compare( + leftPointReader.currentBinary().getStringValue(), + rightPointReader.currentBinary().getStringValue()) + == 0; } } diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareGreaterEqualTransformer.java b/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareGreaterEqualTransformer.java index 7854143cffdd..1e98bfe423cb 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareGreaterEqualTransformer.java +++ b/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareGreaterEqualTransformer.java @@ -29,7 +29,20 @@ public CompareGreaterEqualTransformer( } @Override - protected boolean evaluate(double leftOperand, double rightOperand) { - return Double.compare(leftOperand, rightOperand) >= 0; + protected Evaluator constructNumberEvaluator() { + return () -> + Double.compare( + castCurrentValueToDoubleOperand(leftPointReader, leftPointReaderDataType), + castCurrentValueToDoubleOperand(rightPointReader, rightPointReaderDataType)) + >= 0; + } + + @Override + protected Evaluator constructTextEvaluator() { + return () -> + compare( + leftPointReader.currentBinary().getStringValue(), + rightPointReader.currentBinary().getStringValue()) + >= 0; } } diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareGreaterThanTransformer.java b/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareGreaterThanTransformer.java index 627c4b17bf5c..9e7a5ea3baba 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareGreaterThanTransformer.java +++ b/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareGreaterThanTransformer.java @@ -29,7 +29,20 @@ public CompareGreaterThanTransformer( } @Override - protected boolean evaluate(double leftOperand, double rightOperand) { - return Double.compare(leftOperand, rightOperand) > 0; + protected Evaluator constructNumberEvaluator() { + return () -> + Double.compare( + castCurrentValueToDoubleOperand(leftPointReader, leftPointReaderDataType), + castCurrentValueToDoubleOperand(rightPointReader, rightPointReaderDataType)) + > 0; + } + + @Override + protected Evaluator constructTextEvaluator() { + return () -> + compare( + leftPointReader.currentBinary().getStringValue(), + rightPointReader.currentBinary().getStringValue()) + > 0; } } diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareLessEqualTransformer.java b/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareLessEqualTransformer.java index 2b00dca3fb6a..0c53e869493a 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareLessEqualTransformer.java +++ b/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareLessEqualTransformer.java @@ -29,7 +29,20 @@ public CompareLessEqualTransformer( } @Override - protected boolean evaluate(double leftOperand, double rightOperand) { - return Double.compare(leftOperand, rightOperand) <= 0; + protected Evaluator constructNumberEvaluator() { + return () -> + Double.compare( + castCurrentValueToDoubleOperand(leftPointReader, leftPointReaderDataType), + castCurrentValueToDoubleOperand(rightPointReader, rightPointReaderDataType)) + <= 0; + } + + @Override + protected Evaluator constructTextEvaluator() { + return () -> + compare( + leftPointReader.currentBinary().getStringValue(), + rightPointReader.currentBinary().getStringValue()) + <= 0; } } diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareLessThanTransformer.java b/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareLessThanTransformer.java index ea2ebeded513..9e77e2aa215c 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareLessThanTransformer.java +++ b/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareLessThanTransformer.java @@ -29,7 +29,20 @@ public CompareLessThanTransformer( } @Override - protected boolean evaluate(double leftOperand, double rightOperand) { - return Double.compare(leftOperand, rightOperand) < 0; + protected Evaluator constructNumberEvaluator() { + return () -> + Double.compare( + castCurrentValueToDoubleOperand(leftPointReader, leftPointReaderDataType), + castCurrentValueToDoubleOperand(rightPointReader, rightPointReaderDataType)) + < 0; + } + + @Override + protected Evaluator constructTextEvaluator() { + return () -> + compare( + leftPointReader.currentBinary().getStringValue(), + rightPointReader.currentBinary().getStringValue()) + < 0; } } diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareNonEqualTransformer.java b/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareNonEqualTransformer.java index 795b011bdc54..024c54deb5bb 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareNonEqualTransformer.java +++ b/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareNonEqualTransformer.java @@ -21,7 +21,6 @@ import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; import org.apache.iotdb.tsfile.exception.write.UnSupportedDataTypeException; -import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; public class CompareNonEqualTransformer extends CompareBinaryTransformer { @@ -32,21 +31,20 @@ public CompareNonEqualTransformer( } @Override - protected void checkType() { - if ((leftPointReaderDataType == TSDataType.BOOLEAN - && rightPointReaderDataType != TSDataType.BOOLEAN) - || (leftPointReaderDataType != TSDataType.BOOLEAN - && rightPointReaderDataType == TSDataType.BOOLEAN)) { - throw new UnSupportedDataTypeException( - "left: " - + leftPointReaderDataType.toString() - + ", right: " - + rightPointReaderDataType.toString()); - } + protected Evaluator constructNumberEvaluator() { + return () -> + Double.compare( + castCurrentValueToDoubleOperand(leftPointReader, leftPointReaderDataType), + castCurrentValueToDoubleOperand(rightPointReader, rightPointReaderDataType)) + != 0; } @Override - protected boolean evaluate(double leftOperand, double rightOperand) { - return Double.compare(leftOperand, rightOperand) != 0; + protected Evaluator constructTextEvaluator() { + return () -> + compare( + leftPointReader.currentBinary().getStringValue(), + rightPointReader.currentBinary().getStringValue()) + != 0; } } diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/LogicBinaryTransformer.java b/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/LogicBinaryTransformer.java index d8e1a3601221..7eee752775da 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/LogicBinaryTransformer.java +++ b/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/LogicBinaryTransformer.java @@ -35,13 +35,9 @@ protected LogicBinaryTransformer( @Override protected void checkType() { - if (leftPointReaderDataType != TSDataType.BOOLEAN) { - throw new UnSupportedDataTypeException( - "Unsupported data type: " + leftPointReader.getDataType().toString()); - } - if (rightPointReaderDataType != TSDataType.BOOLEAN) { - throw new UnSupportedDataTypeException( - "Unsupported data type: " + rightPointReader.getDataType().toString()); + if (leftPointReaderDataType != TSDataType.BOOLEAN + || rightPointReaderDataType != TSDataType.BOOLEAN) { + throw new UnSupportedDataTypeException("Unsupported data type: " + TSDataType.BOOLEAN); } } From ec7123b4a61f37ad90cc82227aaf2e734e00837c Mon Sep 17 00:00:00 2001 From: Haonan Date: Sun, 15 May 2022 07:50:43 +0800 Subject: [PATCH 012/436] fix website compile error (#5909) --- site/src/main/.vuepress/config.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/site/src/main/.vuepress/config.js b/site/src/main/.vuepress/config.js index 22c7b086769f..9a9de40a0a36 100644 --- a/site/src/main/.vuepress/config.js +++ b/site/src/main/.vuepress/config.js @@ -703,8 +703,7 @@ var config = { ['API/Programming-MQTT','MQTT'], ['API/RestService','REST API'], ['API/Programming-TsFile-API','TsFile API'], - ['API/Status-Codes','Status Codes'], - ['API/Interface-Comparison', 'Interface Comparison'] + ['API/Status-Codes','Status Codes'] ] }, { @@ -1626,8 +1625,7 @@ var config = { ['API/Programming-MQTT','MQTT'], ['API/RestService','REST API'], ['API/Programming-TsFile-API','TsFile API'], - ['API/Status-Codes','状态码'], - ['API/Interface-Comparison', '原生接口对比'] + ['API/Status-Codes','状态码'] ] }, { From 3857fab2b13f0af282032b8f28cc2035c3f50a27 Mon Sep 17 00:00:00 2001 From: Haonan Date: Sun, 15 May 2022 13:03:07 +0800 Subject: [PATCH 013/436] [IOTDB-3172] Support CreateMultTimeseries API for new cluster (#5896) --- .../visitor/SchemaExecutionVisitor.java | 50 ++ .../iotdb/db/mpp/plan/analyze/Analyzer.java | 15 + .../mpp/plan/parser/StatementGenerator.java | 33 ++ .../db/mpp/plan/planner/LogicalPlanner.java | 17 + .../plan/planner/plan/node/PlanNodeType.java | 6 +- .../plan/planner/plan/node/PlanVisitor.java | 5 + .../write/CreateMultiTimeSeriesNode.java | 453 ++++++++++++++++++ .../mpp/plan/statement/StatementVisitor.java | 7 + .../CreateMultiTimeSeriesStatement.java | 143 ++++++ .../thrift/impl/DataNodeTSIServiceImpl.java | 43 +- .../db/mpp/plan/plan/LogicalPlannerTest.java | 88 ++++ .../db/service/InternalServiceImplTest.java | 100 ++++ 12 files changed, 957 insertions(+), 3 deletions(-) create mode 100644 server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/metedata/write/CreateMultiTimeSeriesNode.java create mode 100644 server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/metadata/CreateMultiTimeSeriesStatement.java diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/visitor/SchemaExecutionVisitor.java b/server/src/main/java/org/apache/iotdb/db/metadata/visitor/SchemaExecutionVisitor.java index 9988e8b8da94..b6b55c731b67 100644 --- a/server/src/main/java/org/apache/iotdb/db/metadata/visitor/SchemaExecutionVisitor.java +++ b/server/src/main/java/org/apache/iotdb/db/metadata/visitor/SchemaExecutionVisitor.java @@ -30,9 +30,11 @@ import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanVisitor; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.write.AlterTimeSeriesNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.write.CreateAlignedTimeSeriesNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.write.CreateMultiTimeSeriesNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.write.CreateTimeSeriesNode; import org.apache.iotdb.db.qp.physical.PhysicalPlan; import org.apache.iotdb.db.qp.physical.sys.CreateAlignedTimeSeriesPlan; +import org.apache.iotdb.db.qp.physical.sys.CreateMultiTimeSeriesPlan; import org.apache.iotdb.db.qp.physical.sys.CreateTimeSeriesPlan; import org.apache.iotdb.rpc.RpcUtils; import org.apache.iotdb.rpc.TSStatusCode; @@ -42,6 +44,7 @@ import org.slf4j.LoggerFactory; import java.io.IOException; +import java.util.Arrays; /** Schema write PlanNode visitor */ public class SchemaExecutionVisitor extends PlanVisitor { @@ -72,6 +75,39 @@ public TSStatus visitCreateAlignedTimeSeries( return RpcUtils.getStatus(TSStatusCode.SUCCESS_STATUS, "Execute successfully"); } + @Override + public TSStatus visitCreateMultiTimeSeries( + CreateMultiTimeSeriesNode node, ISchemaRegion schemaRegion) { + CreateMultiTimeSeriesPlan multiPlan = + (CreateMultiTimeSeriesPlan) + node.accept(new PhysicalPlanTransformer(), new TransformerContext()); + for (int i = 0; i < multiPlan.getPaths().size(); i++) { + if (multiPlan.getResults().containsKey(i) || multiPlan.isExecuted(i)) { + continue; + } + CreateTimeSeriesPlan plan = + new CreateTimeSeriesPlan( + multiPlan.getPaths().get(i), + multiPlan.getDataTypes().get(i), + multiPlan.getEncodings().get(i), + multiPlan.getCompressors().get(i), + multiPlan.getProps() == null ? null : multiPlan.getProps().get(i), + multiPlan.getTags() == null ? null : multiPlan.getTags().get(i), + multiPlan.getAttributes() == null ? null : multiPlan.getAttributes().get(i), + multiPlan.getAlias() == null ? null : multiPlan.getAlias().get(i)); + try { + schemaRegion.createTimeseries(plan, -1); + } catch (MetadataException e) { + logger.error("{}: MetaData error: ", IoTDBConstant.GLOBAL_DB_NAME, e); + multiPlan.getResults().put(i, RpcUtils.getStatus(e.getErrorCode(), e.getMessage())); + } + } + if (!multiPlan.getResults().isEmpty()) { + return RpcUtils.getStatus(Arrays.asList(multiPlan.getFailingStatus())); + } + return RpcUtils.getStatus(TSStatusCode.SUCCESS_STATUS, "Execute successfully"); + } + @Override public TSStatus visitAlterTimeSeries(AlterTimeSeriesNode node, ISchemaRegion schemaRegion) { try { @@ -146,6 +182,20 @@ public PhysicalPlan visitCreateAlignedTimeSeries( node.getTagsList(), node.getAttributesList()); } + + public PhysicalPlan visitCreateMultiTimeSeries( + CreateMultiTimeSeriesNode node, TransformerContext context) { + CreateMultiTimeSeriesPlan multiPlan = new CreateMultiTimeSeriesPlan(); + multiPlan.setPaths(node.getPaths()); + multiPlan.setDataTypes(node.getDataTypes()); + multiPlan.setEncodings(node.getEncodings()); + multiPlan.setCompressors(node.getCompressors()); + multiPlan.setProps(node.getPropsList()); + multiPlan.setAlias(node.getAliasList()); + multiPlan.setTags(node.getTagsList()); + multiPlan.setAttributes(node.getAttributesList()); + return multiPlan; + } } @Override diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analyzer.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analyzer.java index 952238720714..fd6053fd209f 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analyzer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analyzer.java @@ -58,6 +58,7 @@ import org.apache.iotdb.db.mpp.plan.statement.metadata.CountStorageGroupStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.CountTimeSeriesStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.CreateAlignedTimeSeriesStatement; +import org.apache.iotdb.db.mpp.plan.statement.metadata.CreateMultiTimeSeriesStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.CreateTimeSeriesStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.SchemaFetchStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowDevicesStatement; @@ -854,6 +855,20 @@ public Analysis visitCreateAlignedTimeseries( return analysis; } + @Override + public Analysis visitCreateMultiTimeseries( + CreateMultiTimeSeriesStatement createMultiTimeSeriesStatement, MPPQueryContext context) { + context.setQueryType(QueryType.WRITE); + Analysis analysis = new Analysis(); + analysis.setStatement(createMultiTimeSeriesStatement); + + SchemaPartition schemaPartitionInfo = + partitionFetcher.getOrCreateSchemaPartition( + new PathPatternTree(createMultiTimeSeriesStatement.getPaths())); + analysis.setSchemaPartitionInfo(schemaPartitionInfo); + return analysis; + } + @Override public Analysis visitAlterTimeseries( AlterTimeSeriesStatement alterTimeSeriesStatement, MPPQueryContext context) { diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/StatementGenerator.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/StatementGenerator.java index 51726de6ca6e..fdd5f36f91a2 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/StatementGenerator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/StatementGenerator.java @@ -37,6 +37,7 @@ import org.apache.iotdb.db.mpp.plan.statement.crud.InsertTabletStatement; import org.apache.iotdb.db.mpp.plan.statement.crud.QueryStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.CreateAlignedTimeSeriesStatement; +import org.apache.iotdb.db.mpp.plan.statement.metadata.CreateMultiTimeSeriesStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.CreateTimeSeriesStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.SetStorageGroupStatement; import org.apache.iotdb.db.qp.sql.IoTDBSqlParser; @@ -45,6 +46,7 @@ import org.apache.iotdb.db.query.expression.leaf.TimeSeriesOperand; import org.apache.iotdb.db.utils.QueryDataSetUtils; import org.apache.iotdb.service.rpc.thrift.TSCreateAlignedTimeseriesReq; +import org.apache.iotdb.service.rpc.thrift.TSCreateMultiTimeseriesReq; import org.apache.iotdb.service.rpc.thrift.TSCreateTimeseriesReq; import org.apache.iotdb.service.rpc.thrift.TSInsertRecordReq; import org.apache.iotdb.service.rpc.thrift.TSInsertRecordsOfOneDeviceReq; @@ -373,6 +375,37 @@ public static Statement createStatement(TSCreateAlignedTimeseriesReq req) return statement; } + public static Statement createStatement(TSCreateMultiTimeseriesReq req) + throws IllegalPathException { + // construct create multi timeseries statement + List paths = new ArrayList<>(); + for (String path : req.paths) { + paths.add(new PartialPath(path)); + } + List dataTypes = new ArrayList<>(); + for (int dataType : req.dataTypes) { + dataTypes.add(TSDataType.values()[dataType]); + } + List encodings = new ArrayList<>(); + for (int encoding : req.encodings) { + encodings.add(TSEncoding.values()[encoding]); + } + List compressors = new ArrayList<>(); + for (int compressor : req.compressors) { + compressors.add(CompressionType.values()[compressor]); + } + CreateMultiTimeSeriesStatement statement = new CreateMultiTimeSeriesStatement(); + statement.setPaths(paths); + statement.setDataTypes(dataTypes); + statement.setEncodings(encodings); + statement.setCompressors(compressors); + statement.setPropsList(req.propsList); + statement.setTagsList(req.tagsList); + statement.setAttributesList(req.attributesList); + statement.setAliasList(req.measurementAliasList); + return statement; + } + private static Statement invokeParser(String sql, ZoneId zoneId) { ASTVisitor astVisitor = new ASTVisitor(); astVisitor.setZoneId(zoneId); diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanner.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanner.java index befcdb71f4fc..57d0854b953c 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanner.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanner.java @@ -25,6 +25,7 @@ import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.write.AlterTimeSeriesNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.write.CreateAlignedTimeSeriesNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.write.CreateMultiTimeSeriesNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.write.CreateTimeSeriesNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.write.InsertMultiTabletsNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.write.InsertRowNode; @@ -45,6 +46,7 @@ import org.apache.iotdb.db.mpp.plan.statement.metadata.CountLevelTimeSeriesStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.CountTimeSeriesStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.CreateAlignedTimeSeriesStatement; +import org.apache.iotdb.db.mpp.plan.statement.metadata.CreateMultiTimeSeriesStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.CreateTimeSeriesStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.SchemaFetchStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowDevicesStatement; @@ -281,6 +283,21 @@ public PlanNode visitCreateAlignedTimeseries( createAlignedTimeSeriesStatement.getAttributesList()); } + @Override + public PlanNode visitCreateMultiTimeseries( + CreateMultiTimeSeriesStatement createMultiTimeSeriesStatement, MPPQueryContext context) { + return new CreateMultiTimeSeriesNode( + context.getQueryId().genPlanNodeId(), + createMultiTimeSeriesStatement.getPaths(), + createMultiTimeSeriesStatement.getDataTypes(), + createMultiTimeSeriesStatement.getEncodings(), + createMultiTimeSeriesStatement.getCompressors(), + createMultiTimeSeriesStatement.getPropsList(), + createMultiTimeSeriesStatement.getAliasList(), + createMultiTimeSeriesStatement.getTagsList(), + createMultiTimeSeriesStatement.getAttributesList()); + } + @Override public PlanNode visitAlterTimeseries( AlterTimeSeriesStatement alterTimeSeriesStatement, MPPQueryContext context) { diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/PlanNodeType.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/PlanNodeType.java index 1d52cd0231c3..4fe62bad8263 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/PlanNodeType.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/PlanNodeType.java @@ -30,6 +30,7 @@ import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.TimeSeriesSchemaScanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.write.AlterTimeSeriesNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.write.CreateAlignedTimeSeriesNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.write.CreateMultiTimeSeriesNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.write.CreateTimeSeriesNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.process.AggregationNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.process.DeviceViewNode; @@ -99,7 +100,8 @@ public enum PlanNodeType { DEVICE_MERGE((short) 35), SCHEMA_FETCH_MERGE((short) 36), TRANSFORM((short) 37), - DELETE_REGION((short) 38); + DELETE_REGION((short) 38), + CREATE_MULTI_TIME_SERIES((short) 39); private final short nodeType; @@ -201,6 +203,8 @@ public static PlanNode deserialize(ByteBuffer buffer) { return TransformNode.deserialize(buffer); case 38: return DeleteRegionNode.deserialize(buffer); + case 39: + return CreateMultiTimeSeriesNode.deserialize(buffer); default: throw new IllegalArgumentException("Invalid node type: " + nodeType); } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/PlanVisitor.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/PlanVisitor.java index 959cfa3a6286..b11dac5a2668 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/PlanVisitor.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/PlanVisitor.java @@ -30,6 +30,7 @@ import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.TimeSeriesSchemaScanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.write.AlterTimeSeriesNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.write.CreateAlignedTimeSeriesNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.write.CreateMultiTimeSeriesNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.write.CreateTimeSeriesNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.process.AggregationNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.process.DeviceMergeNode; @@ -184,6 +185,10 @@ public R visitCreateAlignedTimeSeries(CreateAlignedTimeSeriesNode node, C contex return visitPlan(node, context); } + public R visitCreateMultiTimeSeries(CreateMultiTimeSeriesNode node, C context) { + return visitPlan(node, context); + } + public R visitAlterTimeSeries(AlterTimeSeriesNode node, C context) { return visitPlan(node, context); } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/metedata/write/CreateMultiTimeSeriesNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/metedata/write/CreateMultiTimeSeriesNode.java new file mode 100644 index 000000000000..52fa45a41137 --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/metedata/write/CreateMultiTimeSeriesNode.java @@ -0,0 +1,453 @@ +/* + * 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.iotdb.db.mpp.plan.planner.plan.node.metedata.write; + +import org.apache.iotdb.common.rpc.thrift.TRegionReplicaSet; +import org.apache.iotdb.commons.exception.IllegalPathException; +import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.db.mpp.plan.analyze.Analysis; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeType; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanVisitor; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.WritePlanNode; +import org.apache.iotdb.tsfile.exception.NotImplementedException; +import org.apache.iotdb.tsfile.file.metadata.enums.CompressionType; +import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; +import org.apache.iotdb.tsfile.file.metadata.enums.TSEncoding; +import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +public class CreateMultiTimeSeriesNode extends WritePlanNode { + private List paths = new ArrayList<>(); + private List dataTypes = new ArrayList<>(); + private List encodings = new ArrayList<>(); + private List compressors = new ArrayList<>(); + private List aliasList; + private List> propsList; + private List> tagsList; + private List> attributesList; + private List tagOffsets; + private TRegionReplicaSet regionReplicaSet; + + public CreateMultiTimeSeriesNode(PlanNodeId id) { + super(id); + } + + public CreateMultiTimeSeriesNode( + PlanNodeId id, + List paths, + List dataTypes, + List encodings, + List compressors, + List> propsList, + List aliasList, + List> tagsList, + List> attributesList) { + super(id); + this.paths = paths; + this.dataTypes = dataTypes; + this.encodings = encodings; + this.compressors = compressors; + this.propsList = propsList; + this.aliasList = aliasList; + this.tagsList = tagsList; + this.attributesList = attributesList; + } + + public List getPaths() { + return paths; + } + + public void setPaths(List paths) { + this.paths = paths; + } + + public List getDataTypes() { + return dataTypes; + } + + public void setDataTypes(List dataTypes) { + this.dataTypes = dataTypes; + } + + public List getEncodings() { + return encodings; + } + + public void setEncodings(List encodings) { + this.encodings = encodings; + } + + public List getCompressors() { + return compressors; + } + + public void setCompressors(List compressors) { + this.compressors = compressors; + } + + public List> getPropsList() { + return propsList; + } + + public void setPropsList(List> propsList) { + this.propsList = propsList; + } + + public List getAliasList() { + return aliasList; + } + + public void setAliasList(List aliasList) { + this.aliasList = aliasList; + } + + public List> getTagsList() { + return tagsList; + } + + public void setTagsList(List> tagsList) { + this.tagsList = tagsList; + } + + public List> getAttributesList() { + return attributesList; + } + + public void setAttributesList(List> attributesList) { + this.attributesList = attributesList; + } + + public List getTagOffsets() { + return tagOffsets; + } + + public void setTagOffsets(List tagOffsets) { + this.tagOffsets = tagOffsets; + } + + public void addTimeSeries( + PartialPath path, + TSDataType dataType, + TSEncoding encoding, + CompressionType compressor, + Map props, + String alias, + Map tags, + Map attributes) { + this.paths.add(path); + this.dataTypes.add(dataType); + this.encodings.add(encoding); + this.compressors.add(compressor); + if (props != null) { + if (this.propsList == null) { + propsList = new ArrayList<>(); + } + propsList.add(props); + } + if (alias != null) { + if (this.aliasList == null) { + aliasList = new ArrayList<>(); + } + aliasList.add(alias); + } + if (tags != null) { + if (this.tagsList == null) { + tagsList = new ArrayList<>(); + } + tagsList.add(tags); + } + if (attributes != null) { + if (this.attributesList == null) { + attributesList = new ArrayList<>(); + } + attributesList.add(attributes); + } + } + + @Override + public List getChildren() { + return new ArrayList<>(); + } + + @Override + public void addChild(PlanNode child) {} + + @Override + public PlanNode clone() { + throw new NotImplementedException("Clone of CreateMultiTimeSeriesNode is not implemented"); + } + + @Override + public int allowedChildCount() { + return NO_CHILD_ALLOWED; + } + + @Override + public List getOutputColumnNames() { + return null; + } + + @Override + public R accept(PlanVisitor visitor, C schemaRegion) { + return visitor.visitCreateMultiTimeSeries(this, schemaRegion); + } + + public static CreateMultiTimeSeriesNode deserialize(ByteBuffer byteBuffer) { + String id; + List paths; + List dataTypes; + List encodings; + List compressors; + List aliasList = null; + List> propsList = null; + List> tagsList = null; + List> attributesList = null; + + int size = byteBuffer.getInt(); + paths = new ArrayList<>(); + for (int i = 0; i < size; i++) { + try { + paths.add(new PartialPath(ReadWriteIOUtils.readString(byteBuffer))); + } catch (IllegalPathException e) { + throw new IllegalArgumentException("Can not deserialize CreateMultiTimeSeriesNode", e); + } + } + + dataTypes = new ArrayList<>(); + for (int i = 0; i < size; i++) { + dataTypes.add(TSDataType.values()[byteBuffer.get()]); + } + + encodings = new ArrayList<>(); + for (int i = 0; i < size; i++) { + encodings.add(TSEncoding.values()[byteBuffer.get()]); + } + + compressors = new ArrayList<>(); + for (int i = 0; i < size; i++) { + compressors.add(CompressionType.values()[byteBuffer.get()]); + } + + byte label = byteBuffer.get(); + if (label >= 0) { + aliasList = new ArrayList<>(); + if (label == 1) { + for (int i = 0; i < size; i++) { + aliasList.add(ReadWriteIOUtils.readString(byteBuffer)); + } + } + } + + label = byteBuffer.get(); + if (label >= 0) { + propsList = new ArrayList<>(); + if (label == 1) { + for (int i = 0; i < size; i++) { + propsList.add(ReadWriteIOUtils.readMap(byteBuffer)); + } + } + } + + label = byteBuffer.get(); + if (label >= 0) { + tagsList = new ArrayList<>(); + if (label == 1) { + for (int i = 0; i < size; i++) { + tagsList.add(ReadWriteIOUtils.readMap(byteBuffer)); + } + } + } + + label = byteBuffer.get(); + if (label >= 0) { + attributesList = new ArrayList<>(); + if (label == 1) { + for (int i = 0; i < size; i++) { + attributesList.add(ReadWriteIOUtils.readMap(byteBuffer)); + } + } + } + + id = ReadWriteIOUtils.readString(byteBuffer); + + return new CreateMultiTimeSeriesNode( + new PlanNodeId(id), + paths, + dataTypes, + encodings, + compressors, + propsList, + aliasList, + tagsList, + attributesList); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + CreateMultiTimeSeriesNode that = (CreateMultiTimeSeriesNode) o; + return this.getPlanNodeId().equals(that.getPlanNodeId()) + && Objects.equals(paths, that.paths) + && Objects.equals(dataTypes, that.dataTypes) + && Objects.equals(encodings, that.encodings) + && Objects.equals(compressors, that.compressors) + && Objects.equals(propsList, that.propsList) + && Objects.equals(tagOffsets, that.tagOffsets) + && Objects.equals(aliasList, that.aliasList) + && Objects.equals(tagsList, that.tagsList) + && Objects.equals(attributesList, that.attributesList); + } + + @Override + protected void serializeAttributes(ByteBuffer byteBuffer) { + PlanNodeType.CREATE_MULTI_TIME_SERIES.serialize(byteBuffer); + + // paths + byteBuffer.putInt(paths.size()); + for (PartialPath path : paths) { + ReadWriteIOUtils.write(path.getFullPath(), byteBuffer); + } + + // dataTypes + for (TSDataType dataType : dataTypes) { + byteBuffer.put((byte) dataType.ordinal()); + } + + // encodings + for (TSEncoding encoding : encodings) { + byteBuffer.put((byte) encoding.ordinal()); + } + + // compressors + for (CompressionType compressor : compressors) { + byteBuffer.put((byte) compressor.ordinal()); + } + + // alias + if (aliasList == null) { + byteBuffer.put((byte) -1); + } else if (aliasList.isEmpty()) { + byteBuffer.put((byte) 0); + } else { + byteBuffer.put((byte) 1); + for (String alias : aliasList) { + ReadWriteIOUtils.write(alias, byteBuffer); + } + } + + // props + if (propsList == null) { + byteBuffer.put((byte) -1); + } else if (propsList.isEmpty()) { + byteBuffer.put((byte) 0); + } else { + byteBuffer.put((byte) 1); + for (Map props : propsList) { + ReadWriteIOUtils.write(props, byteBuffer); + } + } + + // tags + if (tagsList == null) { + byteBuffer.put((byte) -1); + } else if (tagsList.isEmpty()) { + byteBuffer.put((byte) 0); + } else { + byteBuffer.put((byte) 1); + for (Map tags : tagsList) { + ReadWriteIOUtils.write(tags, byteBuffer); + } + } + + // attributes + if (attributesList == null) { + byteBuffer.put((byte) -1); + } else if (attributesList.isEmpty()) { + byteBuffer.put((byte) 0); + } else { + byteBuffer.put((byte) 1); + for (Map attributes : attributesList) { + ReadWriteIOUtils.write(attributes, byteBuffer); + } + } + } + + public int hashCode() { + return Objects.hash( + this.getPlanNodeId(), + paths, + dataTypes, + encodings, + compressors, + tagOffsets, + aliasList, + tagsList, + attributesList); + } + + @Override + public TRegionReplicaSet getRegionReplicaSet() { + return regionReplicaSet; + } + + public void setRegionReplicaSet(TRegionReplicaSet regionReplicaSet) { + this.regionReplicaSet = regionReplicaSet; + } + + @Override + public List splitByPartition(Analysis analysis) { + Map splitMap = new HashMap<>(); + for (int i = 0; i < paths.size(); i++) { + TRegionReplicaSet regionReplicaSet = + analysis.getSchemaPartitionInfo().getSchemaRegionReplicaSet(paths.get(i).getDevice()); + CreateMultiTimeSeriesNode tmpNode; + if (splitMap.containsKey(regionReplicaSet)) { + tmpNode = splitMap.get(regionReplicaSet); + } else { + tmpNode = new CreateMultiTimeSeriesNode(this.getPlanNodeId()); + tmpNode.setRegionReplicaSet(regionReplicaSet); + splitMap.put(regionReplicaSet, tmpNode); + } + tmpNode.addTimeSeries( + paths.get(i), + dataTypes.get(i), + encodings.get(i), + compressors.get(i), + propsList == null ? null : propsList.get(i), + aliasList == null ? null : aliasList.get(i), + attributesList == null ? null : tagsList.get(i), + attributesList == null ? null : attributesList.get(i)); + } + return new ArrayList<>(splitMap.values()); + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/StatementVisitor.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/StatementVisitor.java index 77bd11ebd6b3..16bbdff8dde0 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/StatementVisitor.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/StatementVisitor.java @@ -32,6 +32,7 @@ import org.apache.iotdb.db.mpp.plan.statement.metadata.CountStorageGroupStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.CountTimeSeriesStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.CreateAlignedTimeSeriesStatement; +import org.apache.iotdb.db.mpp.plan.statement.metadata.CreateMultiTimeSeriesStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.CreateTimeSeriesStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.DeleteStorageGroupStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.SchemaFetchStatement; @@ -78,6 +79,12 @@ public R visitCreateAlignedTimeseries( return visitStatement(createAlignedTimeSeriesStatement, context); } + // Create Multi Timeseries + public R visitCreateMultiTimeseries( + CreateMultiTimeSeriesStatement createMultiTimeSeriesStatement, C context) { + return visitStatement(createMultiTimeSeriesStatement, context); + } + // Alter Timeseries public R visitAlterTimeseries(AlterTimeSeriesStatement alterTimeSeriesStatement, C context) { return visitStatement(alterTimeSeriesStatement, context); diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/metadata/CreateMultiTimeSeriesStatement.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/metadata/CreateMultiTimeSeriesStatement.java new file mode 100644 index 000000000000..d46dbc42144d --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/metadata/CreateMultiTimeSeriesStatement.java @@ -0,0 +1,143 @@ +/* + * 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.iotdb.db.mpp.plan.statement.metadata; + +import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.db.mpp.plan.constant.StatementType; +import org.apache.iotdb.db.mpp.plan.statement.Statement; +import org.apache.iotdb.db.mpp.plan.statement.StatementVisitor; +import org.apache.iotdb.tsfile.file.metadata.enums.CompressionType; +import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; +import org.apache.iotdb.tsfile.file.metadata.enums.TSEncoding; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** CREATE MULTI TIMESERIES statement. */ +public class CreateMultiTimeSeriesStatement extends Statement { + + private List paths; + private List dataTypes = new ArrayList<>(); + private List encodings = new ArrayList<>(); + private List compressors = new ArrayList<>(); + private List> propsList; + private List aliasList; + private List> tagsList; + private List> attributesList; + private List tagOffsets; + + public CreateMultiTimeSeriesStatement() { + super(); + statementType = StatementType.CREATE_MULTI_TIMESERIES; + } + + @Override + public List getPaths() { + return paths; + } + + public void setPaths(List paths) { + this.paths = paths; + } + + public List getDataTypes() { + return dataTypes; + } + + public void setDataTypes(List dataTypes) { + this.dataTypes = dataTypes; + } + + public List getEncodings() { + return encodings; + } + + public void setEncodings(List encodings) { + this.encodings = encodings; + } + + public List getCompressors() { + return compressors; + } + + public void setCompressors(List compressors) { + this.compressors = compressors; + } + + public List> getPropsList() { + return propsList; + } + + public void setPropsList(List> propsList) { + this.propsList = propsList; + } + + public List getAliasList() { + return aliasList; + } + + public void setAliasList(List aliasList) { + this.aliasList = aliasList; + } + + public List> getTagsList() { + return tagsList; + } + + public void setTagsList(List> tagsList) { + this.tagsList = tagsList; + } + + public List> getAttributesList() { + return attributesList; + } + + public void setAttributesList(List> attributesList) { + this.attributesList = attributesList; + } + + public void addAttributesList(Map attributes) { + this.attributesList.add(attributes); + } + + public List getTagOffsets() { + if (tagOffsets == null) { + tagOffsets = new ArrayList<>(); + for (int i = 0; i < paths.size(); i++) { + tagOffsets.add(Long.parseLong("-1")); + } + } + return tagOffsets; + } + + public void setTagOffsets(List tagOffsets) { + this.tagOffsets = tagOffsets; + } + + public void addTagOffsets(Long tagsOffset) { + this.tagOffsets.add(tagsOffset); + } + + @Override + public R accept(StatementVisitor visitor, C context) { + return visitor.visitCreateMultiTimeseries(this, context); + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/DataNodeTSIServiceImpl.java b/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/DataNodeTSIServiceImpl.java index c9635fced0a0..f7f863a33cee 100644 --- a/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/DataNodeTSIServiceImpl.java +++ b/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/DataNodeTSIServiceImpl.java @@ -43,6 +43,7 @@ import org.apache.iotdb.db.mpp.plan.statement.crud.InsertRowsStatement; import org.apache.iotdb.db.mpp.plan.statement.crud.InsertTabletStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.CreateAlignedTimeSeriesStatement; +import org.apache.iotdb.db.mpp.plan.statement.metadata.CreateMultiTimeSeriesStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.CreateTimeSeriesStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.SetStorageGroupStatement; import org.apache.iotdb.db.query.control.SessionManager; @@ -367,7 +368,7 @@ public TSStatus createAlignedTimeseries(TSCreateAlignedTimeseriesReq req) { req.getMeasurements()); } - // Step 1: transfer from CreateAlignedTimeSeriesStatement to Statement + // Step 1: transfer from CreateAlignedTimeSeriesReq to Statement CreateAlignedTimeSeriesStatement statement = (CreateAlignedTimeSeriesStatement) StatementGenerator.createStatement(req); @@ -397,7 +398,45 @@ public TSStatus createAlignedTimeseries(TSCreateAlignedTimeseriesReq req) { @Override public TSStatus createMultiTimeseries(TSCreateMultiTimeseriesReq req) { - throw new UnsupportedOperationException(); + try { + if (!SESSION_MANAGER.checkLogin(req.getSessionId())) { + return getNotLoggedInStatus(); + } + + if (AUDIT_LOGGER.isDebugEnabled()) { + AUDIT_LOGGER.debug( + "Session-{} create {} timeseries, the first is {}", + SESSION_MANAGER.getCurrSessionId(), + req.getPaths().size(), + req.getPaths().get(0)); + } + + // Step 1: transfer from CreateMultiTimeSeriesReq to Statement + CreateMultiTimeSeriesStatement statement = + (CreateMultiTimeSeriesStatement) StatementGenerator.createStatement(req); + + // permission check + TSStatus status = AuthorityChecker.checkAuthority(statement, req.sessionId); + if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { + return status; + } + + // Step 2: call the coordinator + long queryId = SESSION_MANAGER.requestQueryId(false); + ExecutionResult result = + COORDINATOR.execute( + statement, + new QueryId(String.valueOf(queryId)), + SESSION_MANAGER.getSessionInfo(req.sessionId), + "", + PARTITION_FETCHER, + SCHEMA_FETCHER); + + return result.status; + } catch (Exception e) { + return onNPEOrUnexpectedException( + e, OperationType.CREATE_TIMESERIES, TSStatusCode.EXECUTE_STATEMENT_ERROR); + } } @Override diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/LogicalPlannerTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/LogicalPlannerTest.java index dec492408150..a0b6261afb3f 100644 --- a/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/LogicalPlannerTest.java +++ b/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/LogicalPlannerTest.java @@ -37,11 +37,14 @@ import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.TimeSeriesSchemaScanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.write.AlterTimeSeriesNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.write.CreateAlignedTimeSeriesNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.write.CreateMultiTimeSeriesNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.write.CreateTimeSeriesNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.process.LimitNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.process.OffsetNode; import org.apache.iotdb.db.mpp.plan.statement.Statement; import org.apache.iotdb.db.mpp.plan.statement.metadata.AlterTimeSeriesStatement; +import org.apache.iotdb.db.mpp.plan.statement.metadata.CreateMultiTimeSeriesStatement; +import org.apache.iotdb.service.rpc.thrift.TSCreateMultiTimeseriesReq; import org.apache.iotdb.tsfile.file.metadata.enums.CompressionType; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.file.metadata.enums.TSEncoding; @@ -187,6 +190,91 @@ public void testCreateAlignedTimeseriesPlan() { } } + @Test + public void testCreateMultiTimeSeriesPlan() { + try { + TSCreateMultiTimeseriesReq req = new TSCreateMultiTimeseriesReq(); + req.setPaths( + new ArrayList() { + { + add("root.sg1.d2.s1"); + add("root.sg1.d2.s2"); + } + }); + req.setMeasurementAliasList( + new ArrayList() { + { + add("meter1"); + add(null); + } + }); + req.setDataTypes( + new ArrayList() { + { + add(TSDataType.FLOAT.ordinal()); + add(TSDataType.FLOAT.ordinal()); + } + }); + req.setEncodings( + new ArrayList() { + { + add(TSEncoding.PLAIN.ordinal()); + add(TSEncoding.PLAIN.ordinal()); + } + }); + req.setCompressors( + new ArrayList() { + { + add(CompressionType.SNAPPY.ordinal()); + add(CompressionType.SNAPPY.ordinal()); + } + }); + req.setAttributesList( + new ArrayList>() { + { + add( + new HashMap() { + { + put("attr1", "a1"); + } + }); + add(null); + } + }); + req.setTagsList( + new ArrayList>() { + { + add( + new HashMap() { + { + put("tag1", "t1"); + } + }); + add(null); + } + }); + CreateMultiTimeSeriesStatement createMultiTimeSeriesStatement = + (CreateMultiTimeSeriesStatement) StatementGenerator.createStatement(req); + MPPQueryContext context = new MPPQueryContext(new QueryId("test_query")); + Analyzer analyzer = + new Analyzer(context, new FakePartitionFetcherImpl(), new FakeSchemaFetcherImpl()); + Analysis analysis = analyzer.analyze(createMultiTimeSeriesStatement); + LogicalPlanner planner = new LogicalPlanner(context, new ArrayList<>()); + CreateMultiTimeSeriesNode createMultiTimeSeriesNode = + (CreateMultiTimeSeriesNode) planner.plan(analysis).getRootNode(); + // Test serialize and deserialize + ByteBuffer byteBuffer = ByteBuffer.allocate(1000); + createMultiTimeSeriesNode.serialize(byteBuffer); + byteBuffer.flip(); + CreateMultiTimeSeriesNode createMultiTimeSeriesNode1 = + (CreateMultiTimeSeriesNode) PlanNodeDeserializeHelper.deserialize(byteBuffer); + Assert.assertEquals(createMultiTimeSeriesNode, createMultiTimeSeriesNode1); + } catch (IllegalPathException e) { + e.printStackTrace(); + fail(); + } + } + @Test public void testAlterTimeseriesPlan() { String sql = "ALTER timeseries root.turbine.d1.s1 RENAME 'tag1' TO 'newTag1'"; diff --git a/server/src/test/java/org/apache/iotdb/db/service/InternalServiceImplTest.java b/server/src/test/java/org/apache/iotdb/db/service/InternalServiceImplTest.java index b20fa5364df2..3fafea7cfdd0 100644 --- a/server/src/test/java/org/apache/iotdb/db/service/InternalServiceImplTest.java +++ b/server/src/test/java/org/apache/iotdb/db/service/InternalServiceImplTest.java @@ -40,6 +40,7 @@ import org.apache.iotdb.db.mpp.plan.planner.plan.PlanFragment; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.write.CreateAlignedTimeSeriesNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.write.CreateMultiTimeSeriesNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.write.CreateTimeSeriesNode; import org.apache.iotdb.db.service.thrift.impl.InternalServiceImpl; import org.apache.iotdb.db.utils.EnvironmentUtils; @@ -252,6 +253,105 @@ public void testCreateAlignedTimeseries() throws MetadataException { Assert.assertTrue(response.accepted); } + @Test + public void testCreateMultiTimeSeries() throws MetadataException { + CreateMultiTimeSeriesNode createMultiTimeSeriesNode = + new CreateMultiTimeSeriesNode( + new PlanNodeId("0"), + new ArrayList() { + { + add(new PartialPath("root.ln.d3.s1")); + add(new PartialPath("root.ln.d3.s2")); + } + }, + new ArrayList() { + { + add(TSDataType.FLOAT); + add(TSDataType.FLOAT); + } + }, + new ArrayList() { + { + add(TSEncoding.PLAIN); + add(TSEncoding.PLAIN); + } + }, + new ArrayList() { + { + add(CompressionType.SNAPPY); + add(CompressionType.SNAPPY); + } + }, + new ArrayList>() { + { + add( + new HashMap() { + { + put("MAX_POINT_NUMBER", "3"); + } + }); + add(null); + } + }, + new ArrayList() { + { + add("meter1"); + add(null); + } + }, + new ArrayList>() { + { + add( + new HashMap() { + { + put("tag1", "t1"); + } + }); + add(null); + } + }, + new ArrayList>() { + { + add( + new HashMap() { + { + put("tag1", "t1"); + } + }); + add(null); + } + }); + + TRegionReplicaSet regionReplicaSet = genRegionReplicaSet(); + PlanFragment planFragment = + new PlanFragment(new PlanFragmentId("2", 3), createMultiTimeSeriesNode); + FragmentInstance fragmentInstance = + new FragmentInstance( + planFragment, + planFragment.getId().genFragmentInstanceId(), + new GroupByFilter(1, 2, 3, 4), + QueryType.WRITE); + fragmentInstance.setDataRegionAndHost(regionReplicaSet); + + // serialize fragmentInstance + ByteBuffer byteBuffer = ByteBuffer.allocate(1024); + fragmentInstance.serializeRequest(byteBuffer); + byteBuffer.flip(); + + // put serialized fragmentInstance to TSendFragmentInstanceReq + TSendFragmentInstanceReq request = new TSendFragmentInstanceReq(); + TFragmentInstance tFragmentInstance = new TFragmentInstance(); + tFragmentInstance.setBody(byteBuffer); + request.setFragmentInstance(tFragmentInstance); + request.setConsensusGroupId(regionReplicaSet.getRegionId()); + request.setQueryType(QueryType.WRITE.toString()); + + // Use consensus layer to execute request + TSendFragmentInstanceResp response = internalServiceImpl.sendFragmentInstance(request); + + Assert.assertTrue(response.accepted); + } + private TRegionReplicaSet genRegionReplicaSet() { List dataNodeList = new ArrayList<>(); dataNodeList.add( From 0a8feecdb93886624349d42a326887f8f3b7628e Mon Sep 17 00:00:00 2001 From: Haonan Date: Sun, 15 May 2022 22:23:38 +0800 Subject: [PATCH 014/436] [IOTDB-3186] Support DeleteStorageGroups API for new cluster (#5912) --- .../mpp/plan/parser/StatementGenerator.java | 7 ++ .../thrift/impl/DataNodeTSIServiceImpl.java | 65 +++++++++++++++---- 2 files changed, 60 insertions(+), 12 deletions(-) diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/StatementGenerator.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/StatementGenerator.java index fdd5f36f91a2..faeb37ef7b41 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/StatementGenerator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/StatementGenerator.java @@ -39,6 +39,7 @@ import org.apache.iotdb.db.mpp.plan.statement.metadata.CreateAlignedTimeSeriesStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.CreateMultiTimeSeriesStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.CreateTimeSeriesStatement; +import org.apache.iotdb.db.mpp.plan.statement.metadata.DeleteStorageGroupStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.SetStorageGroupStatement; import org.apache.iotdb.db.qp.sql.IoTDBSqlParser; import org.apache.iotdb.db.qp.sql.SqlLexer; @@ -406,6 +407,12 @@ public static Statement createStatement(TSCreateMultiTimeseriesReq req) return statement; } + public static Statement createStatement(List storageGroups) { + DeleteStorageGroupStatement statement = new DeleteStorageGroupStatement(); + statement.setPrefixPath(storageGroups); + return statement; + } + private static Statement invokeParser(String sql, ZoneId zoneId) { ASTVisitor astVisitor = new ASTVisitor(); astVisitor.setZoneId(zoneId); diff --git a/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/DataNodeTSIServiceImpl.java b/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/DataNodeTSIServiceImpl.java index f7f863a33cee..c86f88b097d9 100644 --- a/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/DataNodeTSIServiceImpl.java +++ b/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/DataNodeTSIServiceImpl.java @@ -45,6 +45,7 @@ import org.apache.iotdb.db.mpp.plan.statement.metadata.CreateAlignedTimeSeriesStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.CreateMultiTimeSeriesStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.CreateTimeSeriesStatement; +import org.apache.iotdb.db.mpp.plan.statement.metadata.DeleteStorageGroupStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.SetStorageGroupStatement; import org.apache.iotdb.db.query.control.SessionManager; import org.apache.iotdb.db.query.control.SessionTimeoutManager; @@ -298,7 +299,7 @@ public TSStatus setStorageGroup(long sessionId, String storageGroup) { return result.status; } catch (Exception e) { return onNPEOrUnexpectedException( - e, OperationType.CREATE_TIMESERIES, TSStatusCode.EXECUTE_STATEMENT_ERROR); + e, OperationType.SET_STORAGE_GROUP, TSStatusCode.EXECUTE_STATEMENT_ERROR); } } @@ -392,7 +393,7 @@ public TSStatus createAlignedTimeseries(TSCreateAlignedTimeseriesReq req) { return result.status; } catch (Exception e) { return onNPEOrUnexpectedException( - e, OperationType.CREATE_TIMESERIES, TSStatusCode.EXECUTE_STATEMENT_ERROR); + e, OperationType.CREATE_ALIGNED_TIMESERIES, TSStatusCode.EXECUTE_STATEMENT_ERROR); } } @@ -435,7 +436,7 @@ public TSStatus createMultiTimeseries(TSCreateMultiTimeseriesReq req) { return result.status; } catch (Exception e) { return onNPEOrUnexpectedException( - e, OperationType.CREATE_TIMESERIES, TSStatusCode.EXECUTE_STATEMENT_ERROR); + e, OperationType.CREATE_MULTI_TIMESERIES, TSStatusCode.EXECUTE_STATEMENT_ERROR); } } @@ -445,8 +446,46 @@ public TSStatus deleteTimeseries(long sessionId, List path) { } @Override - public TSStatus deleteStorageGroups(long sessionId, List storageGroup) { - throw new UnsupportedOperationException(); + public TSStatus deleteStorageGroups(long sessionId, List storageGroups) { + try { + if (!SESSION_MANAGER.checkLogin(sessionId)) { + return getNotLoggedInStatus(); + } + + if (AUDIT_LOGGER.isDebugEnabled()) { + AUDIT_LOGGER.debug( + "Session-{} delete {} storage groups, the first is {}", + SESSION_MANAGER.getCurrSessionId(), + storageGroups.size(), + storageGroups.get(0)); + } + + // Step 1: transfer from DeleteStorageGroupsReq to Statement + DeleteStorageGroupStatement statement = + (DeleteStorageGroupStatement) StatementGenerator.createStatement(storageGroups); + + // permission check + TSStatus status = AuthorityChecker.checkAuthority(statement, sessionId); + if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { + return status; + } + + // Step 2: call the coordinator + long queryId = SESSION_MANAGER.requestQueryId(false); + ExecutionResult result = + COORDINATOR.execute( + statement, + new QueryId(String.valueOf(queryId)), + SESSION_MANAGER.getSessionInfo(sessionId), + "", + PARTITION_FETCHER, + SCHEMA_FETCHER); + + return result.status; + } catch (Exception e) { + return onNPEOrUnexpectedException( + e, OperationType.DELETE_STORAGE_GROUPS, TSStatusCode.EXECUTE_STATEMENT_ERROR); + } } @Override @@ -599,7 +638,7 @@ public TSStatus insertRecords(TSInsertRecordsReq req) { return result.status; } catch (Exception e) { return onNPEOrUnexpectedException( - e, OperationType.INSERT_TABLET, TSStatusCode.EXECUTE_STATEMENT_ERROR); + e, OperationType.INSERT_RECORDS, TSStatusCode.EXECUTE_STATEMENT_ERROR); } finally { addOperationLatency(Operation.EXECUTE_RPC_BATCH_INSERT, t1); } @@ -645,7 +684,7 @@ public TSStatus insertRecordsOfOneDevice(TSInsertRecordsOfOneDeviceReq req) { return result.status; } catch (Exception e) { return onNPEOrUnexpectedException( - e, OperationType.INSERT_TABLET, TSStatusCode.EXECUTE_STATEMENT_ERROR); + e, OperationType.INSERT_RECORDS_OF_ONE_DEVICE, TSStatusCode.EXECUTE_STATEMENT_ERROR); } finally { addOperationLatency(Operation.EXECUTE_RPC_BATCH_INSERT, t1); } @@ -691,7 +730,9 @@ public TSStatus insertStringRecordsOfOneDevice(TSInsertStringRecordsOfOneDeviceR return result.status; } catch (Exception e) { return onNPEOrUnexpectedException( - e, OperationType.INSERT_TABLET, TSStatusCode.EXECUTE_STATEMENT_ERROR); + e, + OperationType.INSERT_STRING_RECORDS_OF_ONE_DEVICE, + TSStatusCode.EXECUTE_STATEMENT_ERROR); } finally { addOperationLatency(Operation.EXECUTE_RPC_BATCH_INSERT, t1); } @@ -733,7 +774,7 @@ public TSStatus insertRecord(TSInsertRecordReq req) { return result.status; } catch (Exception e) { return onNPEOrUnexpectedException( - e, OperationType.INSERT_TABLET, TSStatusCode.EXECUTE_STATEMENT_ERROR); + e, OperationType.INSERT_RECORD, TSStatusCode.EXECUTE_STATEMENT_ERROR); } finally { addOperationLatency(Operation.EXECUTE_RPC_BATCH_INSERT, t1); } @@ -771,7 +812,7 @@ public TSStatus insertTablets(TSInsertTabletsReq req) { return result.status; } catch (Exception e) { return onNPEOrUnexpectedException( - e, OperationType.INSERT_TABLET, TSStatusCode.EXECUTE_STATEMENT_ERROR); + e, OperationType.INSERT_TABLETS, TSStatusCode.EXECUTE_STATEMENT_ERROR); } finally { addOperationLatency(Operation.EXECUTE_RPC_BATCH_INSERT, t1); } @@ -852,7 +893,7 @@ public TSStatus insertStringRecords(TSInsertStringRecordsReq req) { return result.status; } catch (Exception e) { return onNPEOrUnexpectedException( - e, OperationType.INSERT_TABLET, TSStatusCode.EXECUTE_STATEMENT_ERROR); + e, OperationType.INSERT_STRING_RECORDS, TSStatusCode.EXECUTE_STATEMENT_ERROR); } finally { addOperationLatency(Operation.EXECUTE_RPC_BATCH_INSERT, t1); } @@ -991,7 +1032,7 @@ public TSStatus insertStringRecord(TSInsertStringRecordReq req) { return result.status; } catch (Exception e) { return onNPEOrUnexpectedException( - e, OperationType.INSERT_TABLET, TSStatusCode.EXECUTE_STATEMENT_ERROR); + e, OperationType.INSERT_STRING_RECORD, TSStatusCode.EXECUTE_STATEMENT_ERROR); } finally { addOperationLatency(Operation.EXECUTE_RPC_BATCH_INSERT, t1); } From 9b2a97819e341ca92a3c0998924db9c1dd0a6e22 Mon Sep 17 00:00:00 2001 From: SzyWilliam <48054931+SzyWilliam@users.noreply.github.com> Date: Sun, 15 May 2022 23:07:38 +0800 Subject: [PATCH 015/436] [IOTDB-3167] Add_wait_leader_ready for RatisConsensus and tune params (#5904) * finish * fix code smell * revert log level * adjust default parameter * spotless apply * revise * spotless * add sleep time * add sleep time * comment wait_until_leader_ready logic * comment commit index logic * restore wait_until_leader logic and fix test dependency * rerun ci Co-authored-by: LebronAl --- .../src/assembly/resources/conf/logback.xml | 2 +- .../confignode/manager/ConsensusManager.java | 19 ++++ .../thrift/ConfigNodeRPCServiceProcessor.java | 6 ++ .../ConfigNodeRPCServiceProcessorTest.java | 6 +- .../iotdb/consensus/ratis/RatisClient.java | 6 ++ .../iotdb/consensus/ratis/RatisConsensus.java | 91 ++++++++++++++----- .../consensus/ratis/RatisConsensusTest.java | 74 ++++++++------- .../commons/client/ClientFactoryProperty.java | 2 +- 8 files changed, 145 insertions(+), 61 deletions(-) diff --git a/confignode/src/assembly/resources/conf/logback.xml b/confignode/src/assembly/resources/conf/logback.xml index e5181c56ddb0..9c538adb4d20 100644 --- a/confignode/src/assembly/resources/conf/logback.xml +++ b/confignode/src/assembly/resources/conf/logback.xml @@ -136,5 +136,5 @@ - + diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConsensusManager.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConsensusManager.java index 80064c33fc75..58ffbf297055 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConsensusManager.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConsensusManager.java @@ -23,6 +23,7 @@ import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.commons.consensus.ConsensusGroupId; import org.apache.iotdb.commons.consensus.PartitionRegionId; +import org.apache.iotdb.commons.utils.TestOnly; import org.apache.iotdb.confignode.client.SyncConfigNodeClientPool; import org.apache.iotdb.confignode.conf.ConfigNodeConf; import org.apache.iotdb.confignode.conf.ConfigNodeDescriptor; @@ -62,6 +63,24 @@ public void close() throws IOException { consensusImpl.stop(); } + @TestOnly + public void singleCopyMayWaitUntilLeaderReady() { + if (conf.getConfigNodeList().size() == 1) { + long startTime = System.currentTimeMillis(); + long maxWaitTime = 1000 * 60; // milliseconds, which is 60s + try { + while (!consensusImpl.isLeader(consensusGroupId)) { + Thread.sleep(100); + long elapsed = System.currentTimeMillis() - startTime; + if (elapsed > maxWaitTime) { + return; + } + } + } catch (InterruptedException ignored) { + } + } + } + /** Build ConfigNodeGroup ConsensusLayer */ private void setConsensusLayer() throws IOException { // There is only one ConfigNodeGroup diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessor.java b/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessor.java index 08795217ac1c..2748f83e657a 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessor.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessor.java @@ -46,6 +46,7 @@ import org.apache.iotdb.confignode.consensus.response.SchemaPartitionResp; import org.apache.iotdb.confignode.consensus.response.StorageGroupSchemaResp; import org.apache.iotdb.confignode.manager.ConfigManager; +import org.apache.iotdb.confignode.manager.ConsensusManager; import org.apache.iotdb.confignode.rpc.thrift.ConfigIService; import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerReq; import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerResp; @@ -99,6 +100,11 @@ public void close() throws IOException { configManager.close(); } + @TestOnly + public ConsensusManager getConsensusManager() { + return configManager.getConsensusManager(); + } + @Override public TDataNodeRegisterResp registerDataNode(TDataNodeRegisterReq req) throws TException { RegisterDataNodeReq registerReq = new RegisterDataNodeReq(req.getDataNodeLocation()); diff --git a/confignode/src/test/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessorTest.java b/confignode/src/test/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessorTest.java index 95ae39602064..bc172462ab41 100644 --- a/confignode/src/test/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessorTest.java +++ b/confignode/src/test/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessorTest.java @@ -85,7 +85,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.TimeUnit; public class ConfigNodeRPCServiceProcessorTest { @@ -97,10 +96,9 @@ public static void beforeClass() throws StartupException, ConfigurationException } @Before - public void before() throws IOException, InterruptedException { + public void before() throws IOException { processor = new ConfigNodeRPCServiceProcessor(new ConfigManager()); - // Sleep 1s to make sure the Consensus group has done leader election - TimeUnit.SECONDS.sleep(1); + processor.getConsensusManager().singleCopyMayWaitUntilLeaderReady(); } @After diff --git a/consensus/src/main/java/org/apache/iotdb/consensus/ratis/RatisClient.java b/consensus/src/main/java/org/apache/iotdb/consensus/ratis/RatisClient.java index 8e27109533bb..8d1643bc0ef8 100644 --- a/consensus/src/main/java/org/apache/iotdb/consensus/ratis/RatisClient.java +++ b/consensus/src/main/java/org/apache/iotdb/consensus/ratis/RatisClient.java @@ -28,10 +28,13 @@ import org.apache.ratis.client.RaftClientRpc; import org.apache.ratis.conf.RaftProperties; import org.apache.ratis.protocol.RaftGroup; +import org.apache.ratis.retry.RetryPolicies; +import org.apache.ratis.util.TimeDuration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; +import java.util.concurrent.TimeUnit; public class RatisClient { private final Logger logger = LoggerFactory.getLogger(RatisClient.class); @@ -94,6 +97,9 @@ public PooledObject makeObject(RaftGroup group) throws Exception { RaftClient.newBuilder() .setProperties(raftProperties) .setRaftGroup(group) + .setRetryPolicy( + RetryPolicies.retryForeverWithSleep( + TimeDuration.valueOf(100, TimeUnit.MILLISECONDS))) .setClientRpc(clientRpc) .build(), clientManager)); diff --git a/consensus/src/main/java/org/apache/iotdb/consensus/ratis/RatisConsensus.java b/consensus/src/main/java/org/apache/iotdb/consensus/ratis/RatisConsensus.java index 8d644bbc81fa..74916fba41c1 100644 --- a/consensus/src/main/java/org/apache/iotdb/consensus/ratis/RatisConsensus.java +++ b/consensus/src/main/java/org/apache/iotdb/consensus/ratis/RatisConsensus.java @@ -45,6 +45,7 @@ import org.apache.commons.pool2.KeyedObjectPool; import org.apache.commons.pool2.impl.GenericKeyedObjectPool; import org.apache.ratis.client.RaftClient; +import org.apache.ratis.client.RaftClientConfigKeys; import org.apache.ratis.client.RaftClientRpc; import org.apache.ratis.conf.Parameters; import org.apache.ratis.conf.RaftProperties; @@ -58,9 +59,11 @@ import org.apache.ratis.protocol.RaftGroupId; import org.apache.ratis.protocol.RaftPeer; import org.apache.ratis.protocol.exceptions.NotLeaderException; +import org.apache.ratis.server.DivisionInfo; import org.apache.ratis.server.RaftServer; import org.apache.ratis.server.RaftServerConfigKeys; import org.apache.ratis.util.NetUtils; +import org.apache.ratis.util.TimeDuration; import org.apache.thrift.TException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -72,6 +75,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; @@ -103,6 +107,9 @@ class RatisConsensus implements IConsensus { private static final int DEFAULT_PRIORITY = 0; private static final int LEADER_PRIORITY = 1; + // TODO make it configurable + private static final int DEFAULT_WAIT_LEADER_READY_TIMEOUT = (int) TimeUnit.SECONDS.toMillis(20); + /** * @param ratisStorageDir different groups of RatisConsensus Peer all share ratisStorageDir as * root dir @@ -114,6 +121,14 @@ public RatisConsensus(TEndPoint endpoint, File ratisStorageDir, IStateMachine.Re RaftServerConfigKeys.setStorageDir(properties, Collections.singletonList(ratisStorageDir)); RaftServerConfigKeys.Snapshot.setAutoTriggerEnabled(properties, true); + RaftServerConfigKeys.Rpc.setSlownessTimeout( + properties, TimeDuration.valueOf(10, TimeUnit.MINUTES)); + RaftServerConfigKeys.Rpc.setTimeoutMin(properties, TimeDuration.valueOf(2, TimeUnit.SECONDS)); + RaftServerConfigKeys.Rpc.setTimeoutMax(properties, TimeDuration.valueOf(8, TimeUnit.SECONDS)); + RaftServerConfigKeys.Rpc.setSleepTime(properties, TimeDuration.valueOf(1, TimeUnit.SECONDS)); + + RaftClientConfigKeys.Rpc.setRequestTimeout( + properties, TimeDuration.valueOf(20, TimeUnit.SECONDS)); // set the port which server listen to in RaftProperty object final int port = NetUtils.createSocketAddr(address).getPort(); @@ -149,13 +164,13 @@ public void stop() throws IOException { */ @Override public ConsensusWriteResponse write( - ConsensusGroupId groupId, IConsensusRequest IConsensusRequest) { + ConsensusGroupId consensusGroupId, IConsensusRequest IConsensusRequest) { // pre-condition: group exists and myself server serves this group - RaftGroupId raftGroupId = Utils.fromConsensusGroupIdToRaftGroupId(groupId); + RaftGroupId raftGroupId = Utils.fromConsensusGroupIdToRaftGroupId(consensusGroupId); RaftGroup raftGroup = getGroupInfo(raftGroupId); if (raftGroup == null || !raftGroup.getPeers().contains(myself)) { - return failedWrite(new ConsensusGroupNotExistException(groupId)); + return failedWrite(new ConsensusGroupNotExistException(consensusGroupId)); } // serialize request into Message @@ -163,22 +178,24 @@ public ConsensusWriteResponse write( // 1. first try the local server RaftClientRequest clientRequest = - buildRawRequest(groupId, message, RaftClientRequest.writeRequestType()); + buildRawRequest(raftGroupId, message, RaftClientRequest.writeRequestType()); RaftClientReply localServerReply; RaftPeer suggestedLeader = null; - try { - localServerReply = server.submitClientRequest(clientRequest); - if (localServerReply.isSuccess()) { - ResponseMessage responseMessage = (ResponseMessage) localServerReply.getMessage(); - TSStatus writeStatus = (TSStatus) responseMessage.getContentHolder(); - return ConsensusWriteResponse.newBuilder().setStatus(writeStatus).build(); - } - NotLeaderException ex = localServerReply.getNotLeaderException(); - if (ex != null) { // local server is not leader - suggestedLeader = ex.getSuggestedLeader(); + if (isLeader(consensusGroupId) && waitUntilLeaderReady(raftGroupId)) { + try { + localServerReply = server.submitClientRequest(clientRequest); + if (localServerReply.isSuccess()) { + ResponseMessage responseMessage = (ResponseMessage) localServerReply.getMessage(); + TSStatus writeStatus = (TSStatus) responseMessage.getContentHolder(); + return ConsensusWriteResponse.newBuilder().setStatus(writeStatus).build(); + } + NotLeaderException ex = localServerReply.getNotLeaderException(); + if (ex != null) { // local server is not leader + suggestedLeader = ex.getSuggestedLeader(); + } + } catch (IOException e) { + return failedWrite(new RatisRequestFailedException(e)); } - } catch (IOException e) { - return failedWrite(new RatisRequestFailedException(e)); } // 2. try raft client @@ -209,11 +226,12 @@ public ConsensusWriteResponse write( /** Read directly from LOCAL COPY notice: May read stale data (not linearizable) */ @Override - public ConsensusReadResponse read(ConsensusGroupId groupId, IConsensusRequest IConsensusRequest) { - - RaftGroup group = getGroupInfo(Utils.fromConsensusGroupIdToRaftGroupId(groupId)); + public ConsensusReadResponse read( + ConsensusGroupId consensusGroupId, IConsensusRequest IConsensusRequest) { + RaftGroupId groupId = Utils.fromConsensusGroupIdToRaftGroupId(consensusGroupId); + RaftGroup group = getGroupInfo(groupId); if (group == null || !group.getPeers().contains(myself)) { - return failedRead(new ConsensusGroupNotExistException(groupId)); + return failedRead(new ConsensusGroupNotExistException(consensusGroupId)); } RaftClientReply reply; @@ -476,6 +494,33 @@ public boolean isLeader(ConsensusGroupId groupId) { return isLeader; } + private boolean waitUntilLeaderReady(RaftGroupId groupId) { + DivisionInfo divisionInfo; + try { + divisionInfo = server.getDivision(groupId).getInfo(); + } catch (IOException e) { + // if the query fails, simply return not leader + logger.info("isLeaderReady checking failed with exception: ", e); + return false; + } + long startTime = System.currentTimeMillis(); + try { + while (divisionInfo.isLeader() && !divisionInfo.isLeaderReady()) { + Thread.sleep(10); + long consumedTime = System.currentTimeMillis() - startTime; + if (consumedTime >= DEFAULT_WAIT_LEADER_READY_TIMEOUT) { + logger.warn("{}: leader is still not ready after {}ms", groupId, consumedTime); + return false; + } + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + logger.warn("Unexpected interruption", e); + return false; + } + return divisionInfo.isLeader(); + } + @Override public Peer getLeader(ConsensusGroupId groupId) { if (isLeader(groupId)) { @@ -512,12 +557,12 @@ private ConsensusReadResponse failedRead(ConsensusException e) { } private RaftClientRequest buildRawRequest( - ConsensusGroupId groupId, Message message, RaftClientRequest.Type type) { + RaftGroupId groupId, Message message, RaftClientRequest.Type type) { return RaftClientRequest.newBuilder() .setServerId(server.getId()) .setClientId(localFakeId) .setCallId(localFakeCallId.incrementAndGet()) - .setGroupId(Utils.fromConsensusGroupIdToRaftGroupId(groupId)) + .setGroupId(groupId) .setType(type) .setMessage(message) .build(); @@ -534,7 +579,7 @@ private RaftGroup getGroupInfo(RaftGroupId raftGroupId) { lastSeen.put(raftGroupId, raftGroup); } } catch (IOException e) { - logger.debug("get group failed ", e); + logger.debug("get group {} failed ", raftGroupId, e); } return raftGroup; } diff --git a/consensus/src/test/java/org/apache/iotdb/consensus/ratis/RatisConsensusTest.java b/consensus/src/test/java/org/apache/iotdb/consensus/ratis/RatisConsensusTest.java index 4eee289586e2..bf5b9277c8e0 100644 --- a/consensus/src/test/java/org/apache/iotdb/consensus/ratis/RatisConsensusTest.java +++ b/consensus/src/test/java/org/apache/iotdb/consensus/ratis/RatisConsensusTest.java @@ -56,9 +56,6 @@ public class RatisConsensusTest { private List servers; private List stateMachines; private ConsensusGroup group; - private Peer peer0; - private Peer peer1; - private Peer peer2; CountDownLatch latch; private void makeServers() throws IOException { @@ -83,12 +80,9 @@ private void makeServers() throws IOException { public void setUp() throws IOException { gid = new DataRegionId(1); peers = new ArrayList<>(); - peer0 = new Peer(gid, new TEndPoint("127.0.0.1", 6000)); - peer1 = new Peer(gid, new TEndPoint("127.0.0.1", 6001)); - peer2 = new Peer(gid, new TEndPoint("127.0.0.1", 6002)); - peers.add(peer0); - peers.add(peer1); - peers.add(peer2); + peers.add(new Peer(gid, new TEndPoint("127.0.0.1", 6000))); + peers.add(new Peer(gid, new TEndPoint("127.0.0.1", 6001))); + peers.add(new Peer(gid, new TEndPoint("127.0.0.1", 6002))); peersStorage = new ArrayList<>(); peersStorage.add(new File("./target/1/")); peersStorage.add(new File("./target/2/")); @@ -112,6 +106,10 @@ public void tearDown() throws IOException { } } + private int getLeaderOrdinal() { + return servers.get(0).getLeader(gid).getEndpoint().port - 6000; + } + @Test public void basicConsensus() throws Exception { @@ -123,47 +121,59 @@ public void basicConsensus() throws Exception { // 2. Do Consensus 10 doConsensus(servers.get(0), group.getGroupId(), 10, 10); + int leader = getLeaderOrdinal(); + int follower1 = (leader + 1) % 3; + int follower2 = (leader + 2) % 3; + // 3. Remove two Peers from Group (peer 0 and peer 2) // transfer the leader to peer1 - servers.get(0).transferLeader(gid, peer1); + // servers.get(0).transferLeader(gid, peers.get(1)); // Assert.assertTrue(servers.get(1).isLeader(gid)); // first use removePeer to inform the group leader of configuration change - servers.get(1).removePeer(gid, peer0); - servers.get(1).removePeer(gid, peer2); + servers.get(leader).removePeer(gid, peers.get(follower1)); + servers.get(leader).removePeer(gid, peers.get(follower2)); // then use removeConsensusGroup to clean up removed Consensus-Peer's states - servers.get(0).removeConsensusGroup(gid); - servers.get(2).removeConsensusGroup(gid); - Assert.assertEquals(servers.get(1).getLeader(gid).getEndpoint(), peers.get(1).getEndpoint()); - Assert.assertEquals(stateMachines.get(1).getLeaderEndpoint(), peers.get(1).getEndpoint()); - Assert.assertEquals(stateMachines.get(1).getConfiguration().size(), 1); - Assert.assertEquals(stateMachines.get(1).getConfiguration().get(0), peers.get(1)); + servers.get(follower1).removeConsensusGroup(gid); + servers.get(follower2).removeConsensusGroup(gid); + Assert.assertEquals( + servers.get(leader).getLeader(gid).getEndpoint(), peers.get(leader).getEndpoint()); + Assert.assertEquals( + stateMachines.get(leader).getLeaderEndpoint(), peers.get(leader).getEndpoint()); + Assert.assertEquals(stateMachines.get(leader).getConfiguration().size(), 1); + Assert.assertEquals(stateMachines.get(leader).getConfiguration().get(0), peers.get(leader)); // 4. try consensus again with one peer - doConsensus(servers.get(1), gid, 10, 20); + doConsensus(servers.get(leader), gid, 10, 20); // 5. add two peers back // first notify these new peers, let them initialize - servers.get(0).addConsensusGroup(gid, peers); - servers.get(2).addConsensusGroup(gid, peers); + servers.get(follower1).addConsensusGroup(gid, peers); + servers.get(follower2).addConsensusGroup(gid, peers); // then use addPeer to inform the group leader of configuration change - servers.get(1).addPeer(gid, peer0); - servers.get(1).addPeer(gid, peer2); - Assert.assertEquals(stateMachines.get(1).getConfiguration().size(), 3); + servers.get(leader).addPeer(gid, peers.get(follower1)); + servers.get(leader).addPeer(gid, peers.get(follower2)); + Assert.assertEquals(stateMachines.get(leader).getConfiguration().size(), 3); // 6. try consensus with all 3 peers doConsensus(servers.get(2), gid, 10, 30); + leader = getLeaderOrdinal(); + follower1 = (leader + 1) % 3; + follower2 = (leader + 2) % 3; // 7. again, group contains only peer0 - servers.get(0).transferLeader(group.getGroupId(), peer0); - servers.get(0).changePeer(group.getGroupId(), Collections.singletonList(peer0)); - servers.get(1).removeConsensusGroup(group.getGroupId()); - servers.get(2).removeConsensusGroup(group.getGroupId()); - Assert.assertEquals(stateMachines.get(0).getLeaderEndpoint(), peers.get(0).getEndpoint()); - Assert.assertEquals(stateMachines.get(0).getConfiguration().size(), 1); - Assert.assertEquals(stateMachines.get(0).getConfiguration().get(0), peers.get(0)); + // servers.get(0).transferLeader(group.getGroupId(), peers.get(0)); + servers + .get(leader) + .changePeer(group.getGroupId(), Collections.singletonList(peers.get(leader))); + servers.get(follower1).removeConsensusGroup(group.getGroupId()); + servers.get(follower2).removeConsensusGroup(group.getGroupId()); + Assert.assertEquals( + stateMachines.get(leader).getLeaderEndpoint(), peers.get(leader).getEndpoint()); + Assert.assertEquals(stateMachines.get(leader).getConfiguration().size(), 1); + Assert.assertEquals(stateMachines.get(leader).getConfiguration().get(0), peers.get(leader)); // 8. try consensus with only peer0 - doConsensus(servers.get(0), gid, 10, 40); + doConsensus(servers.get(leader), gid, 10, 40); // 9. shutdown all the servers for (IConsensus consensus : servers) { diff --git a/node-commons/src/main/java/org/apache/iotdb/commons/client/ClientFactoryProperty.java b/node-commons/src/main/java/org/apache/iotdb/commons/client/ClientFactoryProperty.java index f1801628e430..2ff20065e892 100644 --- a/node-commons/src/main/java/org/apache/iotdb/commons/client/ClientFactoryProperty.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/client/ClientFactoryProperty.java @@ -89,7 +89,7 @@ public static class DefaultProperty { private DefaultProperty() {} public static final boolean RPC_THRIFT_COMPRESSED_ENABLED = false; - public static final int CONNECTION_TIMEOUT_MS = (int) TimeUnit.SECONDS.toMillis(20);; + public static final int CONNECTION_TIMEOUT_MS = (int) TimeUnit.SECONDS.toMillis(20); public static final int SELECTOR_NUM_OF_ASYNC_CLIENT_MANAGER = 1; } } From b452368eb9814ed7b1ba3f3287aacb2918b33252 Mon Sep 17 00:00:00 2001 From: Mrquan <50790061+MrQuansy@users.noreply.github.com> Date: Mon, 16 May 2022 08:00:08 +0800 Subject: [PATCH 016/436] Report cpu core and total memory to config node (#5914) * Scripts of stop and remove datanode * [DataNode] Add dataNodeInfo * [DataNode] Add dataNodeInfo --- .../thrift/ConfigNodeRPCServiceProcessorTest.java | 13 +++++++++++-- .../java/org/apache/iotdb/db/service/DataNode.java | 6 ++++++ thrift-commons/src/main/thrift/common.thrift | 5 +++++ thrift-confignode/src/main/thrift/confignode.thrift | 3 ++- 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/confignode/src/test/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessorTest.java b/confignode/src/test/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessorTest.java index bc172462ab41..3853edc0ca1c 100644 --- a/confignode/src/test/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessorTest.java +++ b/confignode/src/test/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessorTest.java @@ -19,6 +19,7 @@ package org.apache.iotdb.confignode.service.thrift; import org.apache.iotdb.common.rpc.thrift.TConsensusGroupType; +import org.apache.iotdb.common.rpc.thrift.TDataNodeInfo; import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation; import org.apache.iotdb.common.rpc.thrift.TEndPoint; import org.apache.iotdb.common.rpc.thrift.TRegionReplicaSet; @@ -137,7 +138,11 @@ private void registerDataNodes() throws TException { dataNodeLocation.setDataBlockManagerEndPoint(new TEndPoint("0.0.0.0", 8777 + i)); dataNodeLocation.setConsensusEndPoint(new TEndPoint("0.0.0.0", 40010 + i)); - TDataNodeRegisterReq req = new TDataNodeRegisterReq(dataNodeLocation); + TDataNodeInfo dataNodeInfo = new TDataNodeInfo(); + dataNodeInfo.setCpuCoreNum(8); + dataNodeInfo.setMaxMemory(1024 * 1024); + + TDataNodeRegisterReq req = new TDataNodeRegisterReq(dataNodeLocation, dataNodeInfo); TDataNodeRegisterResp resp = processor.registerDataNode(req); Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), resp.getStatus().getCode()); @@ -157,7 +162,11 @@ public void registerAndQueryDataNodeTest() throws TException { dataNodeLocation.setDataBlockManagerEndPoint(new TEndPoint("0.0.0.0", 8778)); dataNodeLocation.setConsensusEndPoint(new TEndPoint("0.0.0.0", 40011)); - TDataNodeRegisterReq req = new TDataNodeRegisterReq(dataNodeLocation); + TDataNodeInfo dataNodeInfo = new TDataNodeInfo(); + dataNodeInfo.setCpuCoreNum(8); + dataNodeInfo.setMaxMemory(1024 * 1024); + + TDataNodeRegisterReq req = new TDataNodeRegisterReq(dataNodeLocation, dataNodeInfo); TDataNodeRegisterResp resp = processor.registerDataNode(req); Assert.assertEquals( TSStatusCode.DATANODE_ALREADY_REGISTERED.getStatusCode(), resp.getStatus().getCode()); diff --git a/server/src/main/java/org/apache/iotdb/db/service/DataNode.java b/server/src/main/java/org/apache/iotdb/db/service/DataNode.java index 887515016240..bf77e28b2d5f 100644 --- a/server/src/main/java/org/apache/iotdb/db/service/DataNode.java +++ b/server/src/main/java/org/apache/iotdb/db/service/DataNode.java @@ -19,6 +19,7 @@ package org.apache.iotdb.db.service; import org.apache.iotdb.common.rpc.thrift.TConfigNodeLocation; +import org.apache.iotdb.common.rpc.thrift.TDataNodeInfo; import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation; import org.apache.iotdb.common.rpc.thrift.TEndPoint; import org.apache.iotdb.commons.concurrent.IoTDBDefaultThreadExceptionHandler; @@ -154,6 +155,11 @@ public void joinCluster() throws StartupException { new TEndPoint(config.getInternalIp(), config.getConsensusPort())); req.setDataNodeLocation(location); + TDataNodeInfo info = new TDataNodeInfo(); + info.setCpuCoreNum(Runtime.getRuntime().availableProcessors()); + info.setMaxMemory(Runtime.getRuntime().totalMemory()); + req.setDataNodeInfo(info); + TDataNodeRegisterResp dataNodeRegisterResp = configNodeClient.registerDataNode(req); // store config node lists from resp diff --git a/thrift-commons/src/main/thrift/common.thrift b/thrift-commons/src/main/thrift/common.thrift index 8ffdf3ff4048..5d3b70e9d536 100644 --- a/thrift-commons/src/main/thrift/common.thrift +++ b/thrift-commons/src/main/thrift/common.thrift @@ -80,4 +80,9 @@ struct TDataNodeLocation { struct THeartbeatResp { 1: required i64 heartbeatTimestamp +} + +struct TDataNodeInfo { + 1: required i32 cpuCoreNum + 2: required i64 maxMemory } \ No newline at end of file diff --git a/thrift-confignode/src/main/thrift/confignode.thrift b/thrift-confignode/src/main/thrift/confignode.thrift index 7fa76108e819..29b4a0444905 100644 --- a/thrift-confignode/src/main/thrift/confignode.thrift +++ b/thrift-confignode/src/main/thrift/confignode.thrift @@ -24,9 +24,10 @@ namespace py iotdb.thrift.confignode // DataNode struct TDataNodeRegisterReq { 1: required common.TDataNodeLocation dataNodeLocation + 2: required common.TDataNodeInfo dataNodeInfo // Map // DataNode can use statusMap to report its status to the ConfigNode when restart - 2: optional map statusMap + 3: optional map statusMap } struct TGlobalConfig { From 7942b02c47317e70824764d6181e02857bdaac12 Mon Sep 17 00:00:00 2001 From: YongzaoDan <33111881+CRZbulabula@users.noreply.github.com> Date: Mon, 16 May 2022 09:49:56 +0800 Subject: [PATCH 017/436] [IOTDB-3183] Cancel the singleton mode in the ConfigNode's persistence module (#5913) --- .../PartitionRegionStateMachine.java | 6 +- .../manager/ClusterSchemaManager.java | 18 ++++- .../confignode/manager/ConfigManager.java | 35 +++++++--- .../confignode/manager/ConsensusManager.java | 10 ++- .../iotdb/confignode/manager/Manager.java | 4 +- .../iotdb/confignode/manager/NodeManager.java | 15 ++-- .../confignode/manager/PartitionManager.java | 16 ++++- .../confignode/manager/PermissionManager.java | 8 ++- .../confignode/manager/ProcedureManager.java | 20 +++--- .../confignode/persistence/AuthorInfo.java | 12 ---- .../persistence/ClusterSchemaInfo.java | 68 +++++++++---------- .../confignode/persistence/NodeInfo.java | 15 +--- .../confignode/persistence/PartitionInfo.java | 30 +++----- .../confignode/persistence/ProcedureInfo.java | 13 ---- .../executor/ConfigRequestExecutor.java | 18 +++-- .../procedure/ConfigProcedureStore.java | 9 ++- .../DeleteStorageGroupProcedure.java | 18 +++-- .../procedure/DeleteStorageGroupState.java | 2 +- .../DeviceGroupHashExecutorManualTest.java | 3 +- .../persistence/AuthorInfoTest.java | 2 +- .../persistence/ClusterSchemaInfoTest.java | 2 +- .../confignode/persistence/NodeInfoTest.java | 2 +- .../persistence/PartitionInfoTest.java | 2 +- .../ConfigNodeRPCServiceProcessorTest.java | 8 +-- 24 files changed, 170 insertions(+), 166 deletions(-) diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/statemachine/PartitionRegionStateMachine.java b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/statemachine/PartitionRegionStateMachine.java index 1c87318a6338..26a077d376bf 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/statemachine/PartitionRegionStateMachine.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/statemachine/PartitionRegionStateMachine.java @@ -45,10 +45,10 @@ public class PartitionRegionStateMachine implements IStateMachine, IStateMachine private static final Logger LOGGER = LoggerFactory.getLogger(PartitionRegionStateMachine.class); private final ConfigRequestExecutor executor; private ConfigManager configManager; - private TEndPoint currentNode; + private final TEndPoint currentNode; - public PartitionRegionStateMachine(ConfigManager configManager) { - this.executor = new ConfigRequestExecutor(); + public PartitionRegionStateMachine(ConfigManager configManager, ConfigRequestExecutor executor) { + this.executor = executor; this.configManager = configManager; this.currentNode = new TEndPoint() diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/ClusterSchemaManager.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/ClusterSchemaManager.java index 7c0ece755393..baf67a015a99 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/ClusterSchemaManager.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/ClusterSchemaManager.java @@ -41,18 +41,19 @@ import org.slf4j.LoggerFactory; import java.util.List; +import java.util.Map; /** The ClusterSchemaManager Manages cluster schema read and write requests. */ public class ClusterSchemaManager { private static final Logger LOGGER = LoggerFactory.getLogger(ClusterSchemaManager.class); - private static final ClusterSchemaInfo clusterSchemaInfo = ClusterSchemaInfo.getInstance(); - private final Manager configManager; + private final ClusterSchemaInfo clusterSchemaInfo; - public ClusterSchemaManager(Manager configManager) { + public ClusterSchemaManager(Manager configManager, ClusterSchemaInfo clusterSchemaInfo) { this.configManager = configManager; + this.clusterSchemaInfo = clusterSchemaInfo; } /** @@ -106,6 +107,17 @@ public TStorageGroupSchema getStorageGroupSchemaByName(String storageGroup) return clusterSchemaInfo.getMatchedStorageGroupSchemaByName(storageGroup); } + /** + * Only leader use this interface. + * + * @param rawPathList List + * @return the matched StorageGroupSchemas + */ + public Map getMatchedStorageGroupSchemasByName( + List rawPathList) { + return clusterSchemaInfo.getMatchedStorageGroupSchemasByName(rawPathList); + } + public TSStatus setTTL(SetTTLReq setTTLReq) { // TODO: Inform DataNodes return getConsensusManager().write(setTTLReq).getStatus(); diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java index 278859b9c45b..69561b7fcd19 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java @@ -48,9 +48,14 @@ import org.apache.iotdb.confignode.consensus.response.PermissionInfoResp; import org.apache.iotdb.confignode.consensus.response.SchemaPartitionResp; import org.apache.iotdb.confignode.consensus.response.StorageGroupSchemaResp; +import org.apache.iotdb.confignode.consensus.statemachine.PartitionRegionStateMachine; import org.apache.iotdb.confignode.manager.load.LoadManager; +import org.apache.iotdb.confignode.persistence.AuthorInfo; import org.apache.iotdb.confignode.persistence.ClusterSchemaInfo; import org.apache.iotdb.confignode.persistence.NodeInfo; +import org.apache.iotdb.confignode.persistence.PartitionInfo; +import org.apache.iotdb.confignode.persistence.ProcedureInfo; +import org.apache.iotdb.confignode.persistence.executor.ConfigRequestExecutor; import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeRegisterReq; import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeRegisterResp; import org.apache.iotdb.confignode.rpc.thrift.TStorageGroupSchema; @@ -95,13 +100,27 @@ public class ConfigManager implements Manager { private final ProcedureManager procedureManager; public ConfigManager() throws IOException { - this.nodeManager = new NodeManager(this); - this.partitionManager = new PartitionManager(this); - this.clusterSchemaManager = new ClusterSchemaManager(this); - this.permissionManager = new PermissionManager(this); + // Build the persistence module + NodeInfo nodeInfo = new NodeInfo(); + ClusterSchemaInfo clusterSchemaInfo = new ClusterSchemaInfo(); + PartitionInfo partitionInfo = new PartitionInfo(); + AuthorInfo authorInfo = new AuthorInfo(); + ProcedureInfo procedureInfo = new ProcedureInfo(); + + // Build state machine and executor + ConfigRequestExecutor executor = + new ConfigRequestExecutor( + nodeInfo, clusterSchemaInfo, partitionInfo, authorInfo, procedureInfo); + PartitionRegionStateMachine stateMachine = new PartitionRegionStateMachine(this, executor); + + // Build the manager module + this.nodeManager = new NodeManager(this, nodeInfo); + this.clusterSchemaManager = new ClusterSchemaManager(this, clusterSchemaInfo); + this.partitionManager = new PartitionManager(this, partitionInfo); + this.permissionManager = new PermissionManager(this, authorInfo); + this.procedureManager = new ProcedureManager(this, procedureInfo); this.loadManager = new LoadManager(this); - this.procedureManager = new ProcedureManager(this); - this.consensusManager = new ConsensusManager(this); + this.consensusManager = new ConsensusManager(stateMachine); // We are on testing....... if (ConfigNodeDescriptor.getInstance().getConf().isEnableHeartbeat()) { @@ -128,7 +147,7 @@ public DataSet registerDataNode(RegisterDataNodeReq registerDataNodeReq) { } else { DataNodeConfigurationResp dataSet = new DataNodeConfigurationResp(); dataSet.setStatus(status); - dataSet.setConfigNodeList(NodeInfo.getInstance().getOnlineConfigNodes()); + dataSet.setConfigNodeList(nodeManager.getOnlineConfigNodes()); return dataSet; } } @@ -228,7 +247,7 @@ public TSStatus deleteStorageGroups(List deletedPaths) { if (status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) { // remove wild Map deleteStorageSchemaMap = - ClusterSchemaInfo.getInstance().getDeleteStorageGroups(deletedPaths); + getClusterSchemaManager().getMatchedStorageGroupSchemasByName(deletedPaths); for (Map.Entry storageGroupSchemaEntry : deleteStorageSchemaMap.entrySet()) { String sgName = storageGroupSchemaEntry.getKey(); diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConsensusManager.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConsensusManager.java index 58ffbf297055..702f5cfa0577 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConsensusManager.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConsensusManager.java @@ -50,13 +50,11 @@ public class ConsensusManager { private static final Logger LOGGER = LoggerFactory.getLogger(ConsensusManager.class); private static final ConfigNodeConf conf = ConfigNodeDescriptor.getInstance().getConf(); - private final ConfigManager configManager; private ConsensusGroupId consensusGroupId; private IConsensus consensusImpl; - public ConsensusManager(ConfigManager configManager) throws IOException { - this.configManager = configManager; - setConsensusLayer(); + public ConsensusManager(PartitionRegionStateMachine stateMachine) throws IOException { + setConsensusLayer(stateMachine); } public void close() throws IOException { @@ -82,7 +80,7 @@ public void singleCopyMayWaitUntilLeaderReady() { } /** Build ConfigNodeGroup ConsensusLayer */ - private void setConsensusLayer() throws IOException { + private void setConsensusLayer(PartitionRegionStateMachine stateMachine) throws IOException { // There is only one ConfigNodeGroup consensusGroupId = new PartitionRegionId(conf.getPartitionRegionId()); @@ -92,7 +90,7 @@ private void setConsensusLayer() throws IOException { conf.getConfigNodeConsensusProtocolClass(), new TEndPoint(conf.getRpcAddress(), conf.getConsensusPort()), new File(conf.getConsensusDir()), - gid -> new PartitionRegionStateMachine(configManager)) + gid -> stateMachine) .orElseThrow( () -> new IllegalArgumentException( diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/Manager.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/Manager.java index 6a7fc0a35fef..dbb95ca560ac 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/Manager.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/Manager.java @@ -132,9 +132,9 @@ public interface Manager { TSStatus setStorageGroup(SetStorageGroupReq setStorageGroupReq); /** - * Delete StorageGroup + * Delete StorageGroups * - * @param deleteStorageGroupsReq deleteStorageGroupReq + * @param deletedPaths List * @return status */ TSStatus deleteStorageGroups(List deletedPaths); diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/NodeManager.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/NodeManager.java index b5d778e8b568..ea56c4cf0908 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/NodeManager.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/NodeManager.java @@ -18,6 +18,7 @@ */ package org.apache.iotdb.confignode.manager; +import org.apache.iotdb.common.rpc.thrift.TConfigNodeLocation; import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation; import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.commons.consensus.ConsensusGroupId; @@ -47,15 +48,15 @@ public class NodeManager { private static final Logger LOGGER = LoggerFactory.getLogger(NodeManager.class); - private static final NodeInfo nodeInfo = NodeInfo.getInstance(); - private final Manager configManager; + private final NodeInfo nodeInfo; /** TODO:do some operate after add node or remove node */ private final List listeners = new CopyOnWriteArrayList<>(); - public NodeManager(Manager configManager) { + public NodeManager(Manager configManager, NodeInfo nodeInfo) { this.configManager = configManager; + this.nodeInfo = nodeInfo; } private void setGlobalConfig(DataNodeConfigurationResp dataSet) { @@ -82,7 +83,7 @@ private void setGlobalConfig(DataNodeConfigurationResp dataSet) { public DataSet registerDataNode(RegisterDataNodeReq req) { DataNodeConfigurationResp dataSet = new DataNodeConfigurationResp(); - if (NodeInfo.getInstance().containsValue(req.getLocation())) { + if (nodeInfo.containsValue(req.getLocation())) { // Reset client AsyncDataNodeClientPool.getInstance().resetClient(req.getLocation().getInternalEndPoint()); @@ -91,7 +92,7 @@ public DataSet registerDataNode(RegisterDataNodeReq req) { dataSet.setStatus(status); } else { // Persist DataNodeInfo - req.getLocation().setDataNodeId(NodeInfo.getInstance().generateNextDataNodeId()); + req.getLocation().setDataNodeId(nodeInfo.generateNextDataNodeId()); ConsensusWriteResponse resp = getConsensusManager().write(req); dataSet.setStatus(resp.getStatus()); } @@ -167,6 +168,10 @@ public TSStatus applyConfigNode(ApplyConfigNodeReq applyConfigNodeReq) { } } + public List getOnlineConfigNodes() { + return nodeInfo.getOnlineConfigNodes(); + } + private ConsensusManager getConsensusManager() { return configManager.getConsensusManager(); } diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/PartitionManager.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/PartitionManager.java index f0db7b0d0522..ee6e6f35202d 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/PartitionManager.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/PartitionManager.java @@ -57,14 +57,14 @@ public class PartitionManager { private static final Logger LOGGER = LoggerFactory.getLogger(PartitionManager.class); - private static final PartitionInfo partitionInfo = PartitionInfo.getInstance(); - private final Manager configManager; + private final PartitionInfo partitionInfo; private SeriesPartitionExecutor executor; - public PartitionManager(Manager configManager) { + public PartitionManager(Manager configManager, PartitionInfo partitionInfo) { this.configManager = configManager; + this.partitionInfo = partitionInfo; setSeriesPartitionExecutor(); } @@ -294,6 +294,16 @@ public int generateNextRegionGroupId() { return partitionInfo.generateNextRegionGroupId(); } + /** + * Only leader use this interface. + * + * @param groupIds List + * @return RegionReplicaSet by the specific TConsensusGroupIds + */ + public List getRegionReplicaSets(List groupIds) { + return partitionInfo.getRegionReplicaSets(groupIds); + } + private ConsensusManager getConsensusManager() { return configManager.getConsensusManager(); } diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/PermissionManager.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/PermissionManager.java index 61ef6a614a5d..7cbae5a3d8d5 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/PermissionManager.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/PermissionManager.java @@ -30,9 +30,11 @@ public class PermissionManager { private final Manager configManager; + private final AuthorInfo authorInfo; - public PermissionManager(Manager configManager) { + public PermissionManager(Manager configManager, AuthorInfo authorInfo) { this.configManager = configManager; + this.authorInfo = authorInfo; } /** @@ -60,10 +62,10 @@ private ConsensusManager getConsensusManager() { } public TSStatus login(String username, String password) { - return AuthorInfo.getInstance().login(username, password); + return authorInfo.login(username, password); } public TSStatus checkUserPrivileges(String username, List paths, int permission) { - return AuthorInfo.getInstance().checkUserPrivileges(username, paths, permission); + return authorInfo.checkUserPrivileges(username, paths, permission); } } diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/ProcedureManager.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/ProcedureManager.java index d120a9ca04b4..f433e6923408 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/ProcedureManager.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/ProcedureManager.java @@ -23,6 +23,7 @@ import org.apache.iotdb.commons.utils.StatusUtils; import org.apache.iotdb.confignode.conf.ConfigNodeConf; import org.apache.iotdb.confignode.conf.ConfigNodeDescriptor; +import org.apache.iotdb.confignode.persistence.ProcedureInfo; import org.apache.iotdb.confignode.procedure.ConfigProcedureStore; import org.apache.iotdb.confignode.procedure.DeleteStorageGroupProcedure; import org.apache.iotdb.confignode.procedure.env.ConfigNodeProcedureEnv; @@ -44,19 +45,22 @@ public class ProcedureManager { private static final Logger LOGGER = LoggerFactory.getLogger(ProcedureManager.class); + + private static final ConfigNodeConf configNodeConf = ConfigNodeDescriptor.getInstance().getConf(); + private static final int procedureWaitTimeOut = 30; private static final int procedureWaitRetryTimeout = 250; - private final ConfigManager configNodeManager; + + private final ConfigManager configManager; private ProcedureExecutor executor; private ProcedureScheduler scheduler; private IProcedureStore store; private ConfigNodeProcedureEnv env; - private ConfigNodeConf configNodeConf = ConfigNodeDescriptor.getInstance().getConf(); - public ProcedureManager(ConfigManager configManager) { - this.configNodeManager = configManager; + public ProcedureManager(ConfigManager configManager, ProcedureInfo procedureInfo) { + this.configManager = configManager; this.scheduler = new SimpleProcedureScheduler(); - this.store = new ConfigProcedureStore(configManager); + this.store = new ConfigProcedureStore(configManager, procedureInfo); this.env = new ConfigNodeProcedureEnv(configManager); this.executor = new ProcedureExecutor<>(env, store, scheduler); } @@ -86,7 +90,7 @@ public TSStatus deleteStorageGroups(ArrayList deleteSgSchem List procIdList = new ArrayList<>(); for (TStorageGroupSchema storageGroupSchema : deleteSgSchemaList) { DeleteStorageGroupProcedure deleteStorageGroupProcedure = - new DeleteStorageGroupProcedure(storageGroupSchema); + new DeleteStorageGroupProcedure(storageGroupSchema, configManager); long procId = this.executor.submitProcedure(deleteStorageGroupProcedure); procIdList.add(procId); } @@ -145,8 +149,8 @@ public static void sleepWithoutInterrupt(final long timeToSleep) { GET-SET Region */ // ====================================================== - public Manager getConfigNodeManager() { - return configNodeManager; + public Manager getConfigManager() { + return configManager; } public ProcedureExecutor getExecutor() { diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/AuthorInfo.java b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/AuthorInfo.java index 72704fd7da50..3c8563b10fb0 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/AuthorInfo.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/AuthorInfo.java @@ -340,18 +340,6 @@ public PermissionInfoResp executeListUserPrivileges(AuthorReq plan) throws AuthE } } - private static class AuthorInfoHolder { - private static final AuthorInfo INSTANCE = new AuthorInfo(); - - private AuthorInfoHolder() { - // empty constructor - } - } - - public static AuthorInfo getInstance() { - return AuthorInfo.AuthorInfoHolder.INSTANCE; - } - @Override public boolean processTakeSnapshot(File snapshotDir) throws TException, IOException { return authorizer.processTakeSnapshot(snapshotDir); diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/ClusterSchemaInfo.java b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/ClusterSchemaInfo.java index 04a5ef47a95a..9bc8e67d59ef 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/ClusterSchemaInfo.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/ClusterSchemaInfo.java @@ -77,7 +77,7 @@ public class ClusterSchemaInfo implements SnapshotProcessor { private final String snapshotFileName = "cluster_schema.bin"; - private ClusterSchemaInfo() { + public ClusterSchemaInfo() { storageGroupReadWriteLock = new ReentrantReadWriteLock(); try { @@ -136,7 +136,6 @@ public TSStatus deleteStorageGroup(DeleteStorageGroupReq req) { TStorageGroupSchema storageGroupSchema = req.getStorageGroup(); PartialPath partialPathName = new PartialPath(storageGroupSchema.getName()); mTree.deleteStorageGroup(partialPathName); - PartitionInfo.getInstance().deleteStorageGroup(req); result.setCode(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } catch (MetadataException e) { LOGGER.warn("Storage group not exist", e); @@ -345,27 +344,6 @@ public StorageGroupSchemaResp getMatchedStorageGroupSchemas(GetStorageGroupReq r return result; } - /** @return All StorageGroupSchemas that matches to the specific StorageGroup patterns */ - public Map getDeleteStorageGroups(List rawPathList) { - Map schemaMap = new HashMap<>(); - storageGroupReadWriteLock.readLock().lock(); - try { - for (String rawPath : rawPathList) { - PartialPath patternPath = new PartialPath(rawPath); - List matchedPaths = mTree.getBelongedStorageGroups(patternPath); - for (PartialPath path : matchedPaths) { - schemaMap.put( - path.getFullPath(), mTree.getStorageGroupNodeByPath(path).getStorageGroupSchema()); - } - } - } catch (MetadataException e) { - LOGGER.warn("Error StorageGroup name", e); - } finally { - storageGroupReadWriteLock.readLock().unlock(); - } - return schemaMap; - } - /** @return True if StorageGroupInfo contains the specific StorageGroup */ public boolean containsStorageGroup(String storageName) { boolean result; @@ -400,6 +378,28 @@ public TStorageGroupSchema getMatchedStorageGroupSchemaByName(String storageGrou } } + /** @return All StorageGroupSchemas that matches to the specific StorageGroup patterns */ + public Map getMatchedStorageGroupSchemasByName( + List rawPathList) { + Map schemaMap = new HashMap<>(); + storageGroupReadWriteLock.readLock().lock(); + try { + for (String rawPath : rawPathList) { + PartialPath patternPath = new PartialPath(rawPath); + List matchedPaths = mTree.getBelongedStorageGroups(patternPath); + for (PartialPath path : matchedPaths) { + schemaMap.put( + path.getFullPath(), mTree.getStorageGroupNodeByPath(path).getStorageGroupSchema()); + } + } + } catch (MetadataException e) { + LOGGER.warn("Error StorageGroup name", e); + } finally { + storageGroupReadWriteLock.readLock().unlock(); + } + return schemaMap; + } + /** * Get the SchemaRegionGroupIds or DataRegionGroupIds from the specific StorageGroup. * @@ -459,7 +459,14 @@ public boolean processTakeSnapshot(File snapshotDir) throws IOException { return tmpFile.renameTo(snapshotFile); } finally { buffer.clear(); - tmpFile.delete(); + for (int retry = 0; retry < 5; retry++) { + if (tmpFile.delete()) { + break; + } else { + LOGGER.warn( + "Can't delete temporary snapshot file: {}, retrying...", tmpFile.getAbsolutePath()); + } + } storageGroupReadWriteLock.readLock().unlock(); } } @@ -493,17 +500,4 @@ public void processLoadSnapshot(File snapshotDir) throws IOException { public void clear() { mTree.clear(); } - - private static class StorageGroupInfoHolder { - - private static final ClusterSchemaInfo INSTANCE = new ClusterSchemaInfo(); - - private StorageGroupInfoHolder() { - // Empty constructor - } - } - - public static ClusterSchemaInfo getInstance() { - return StorageGroupInfoHolder.INSTANCE; - } } diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/NodeInfo.java b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/NodeInfo.java index 8fe90d1c0ca1..343ec9e66753 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/NodeInfo.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/NodeInfo.java @@ -98,7 +98,7 @@ public class NodeInfo implements SnapshotProcessor { private final String snapshotFileName = "node_info.bin"; - private NodeInfo() { + public NodeInfo() { this.dataNodeInfoReadWriteLock = new ReentrantReadWriteLock(); this.configNodeInfoReadWriteLock = new ReentrantReadWriteLock(); this.onlineConfigNodes = @@ -407,17 +407,4 @@ public void clear() { drainingDataNodes.clear(); onlineConfigNodes.clear(); } - - private static class DataNodeInfoPersistenceHolder { - - private static final NodeInfo INSTANCE = new NodeInfo(); - - private DataNodeInfoPersistenceHolder() { - // empty constructor - } - } - - public static NodeInfo getInstance() { - return NodeInfo.DataNodeInfoPersistenceHolder.INSTANCE; - } } diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/PartitionInfo.java b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/PartitionInfo.java index 1e36ebb8326b..20a701ef34a7 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/PartitionInfo.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/PartitionInfo.java @@ -92,7 +92,7 @@ public class PartitionInfo implements SnapshotProcessor { private final String snapshotFileName = "partition_info.bin"; - private PartitionInfo() { + public PartitionInfo() { this.regionReadWriteLock = new ReentrantReadWriteLock(); this.regionMap = new HashMap<>(); @@ -171,12 +171,11 @@ public TSStatus deleteRegions(DeleteRegionsReq req) { } /** - * Delete Regions + * Delete StorageGroup * * @param req DeleteRegionsReq - * @return SUCCESS_STATUS */ - public TSStatus deleteStorageGroup(DeleteStorageGroupReq req) { + public void deleteStorageGroup(DeleteStorageGroupReq req) { TStorageGroupSchema storageGroupSchema = req.getStorageGroup(); List dataRegionGroupIds = storageGroupSchema.getDataRegionGroupIds(); List schemaRegionGroupIds = storageGroupSchema.getSchemaRegionGroupIds(); @@ -190,7 +189,6 @@ public TSStatus deleteStorageGroup(DeleteStorageGroupReq req) { deleteRegions(deleteRegionsReq); deleteDataPartitionMapByStorageGroup(storageGroupSchema.getName()); deleteSchemaPartitionMapByStorageGroup(storageGroupSchema.getName()); - return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } /** @@ -416,7 +414,14 @@ public boolean processTakeSnapshot(File snapshotDir) throws TException, IOExcept unlockAllRead(); byteBuffer.clear(); // with or without success, delete temporary files anyway - tmpFile.delete(); + for (int retry = 0; retry < 5; retry++) { + if (tmpFile.delete()) { + break; + } else { + LOGGER.warn( + "Can't delete temporary snapshot file: {}, retrying...", tmpFile.getAbsolutePath()); + } + } } } @@ -529,17 +534,4 @@ public void clear() { dataPartition.getDataPartitionMap().clear(); } } - - private static class PartitionInfoHolder { - - private static final PartitionInfo INSTANCE = new PartitionInfo(); - - private PartitionInfoHolder() { - // empty constructor - } - } - - public static PartitionInfo getInstance() { - return PartitionInfoHolder.INSTANCE; - } } diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/ProcedureInfo.java b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/ProcedureInfo.java index 880556015af9..1a0561a5513d 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/ProcedureInfo.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/ProcedureInfo.java @@ -48,10 +48,6 @@ public class ProcedureInfo { CommonDescriptor.getInstance().getConfig().getProcedureWalFolder(); private final ConcurrentHashMap procWALMap = new ConcurrentHashMap<>(); - public static ProcedureInfo getInstance() { - return ProcedureInfoHolder.INSTANCE; - } - public void load(List procedureList) { try { Files.list(Paths.get(procedureWalDir)) @@ -103,13 +99,4 @@ public TSStatus deleteProcedure(DeleteProcedureReq deleteProcedureReq) { procWALMap.remove(procId); return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } - - private static class ProcedureInfoHolder { - - private static final ProcedureInfo INSTANCE = new ProcedureInfo(); - - private ProcedureInfoHolder() { - // Empty constructor - } - } } diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigRequestExecutor.java b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigRequestExecutor.java index 2deed2156902..66b3872e12d0 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigRequestExecutor.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigRequestExecutor.java @@ -75,12 +75,17 @@ public class ConfigRequestExecutor { private final ProcedureInfo procedureInfo; - public ConfigRequestExecutor() { - this.nodeInfo = NodeInfo.getInstance(); - this.clusterSchemaInfo = ClusterSchemaInfo.getInstance(); - this.partitionInfo = PartitionInfo.getInstance(); - this.authorInfo = AuthorInfo.getInstance(); - this.procedureInfo = ProcedureInfo.getInstance(); + public ConfigRequestExecutor( + NodeInfo nodeInfo, + ClusterSchemaInfo clusterSchemaInfo, + PartitionInfo partitionInfo, + AuthorInfo authorInfo, + ProcedureInfo procedureInfo) { + this.nodeInfo = nodeInfo; + this.clusterSchemaInfo = clusterSchemaInfo; + this.partitionInfo = partitionInfo; + this.authorInfo = authorInfo; + this.procedureInfo = procedureInfo; } public DataSet executorQueryPlan(ConfigRequest req) @@ -123,6 +128,7 @@ public TSStatus executorNonQueryPlan(ConfigRequest req) case SetStorageGroup: return clusterSchemaInfo.setStorageGroup((SetStorageGroupReq) req); case DeleteStorageGroup: + partitionInfo.deleteStorageGroup((DeleteStorageGroupReq) req); return clusterSchemaInfo.deleteStorageGroup((DeleteStorageGroupReq) req); case SetTTL: return clusterSchemaInfo.setTTL((SetTTLReq) req); diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/procedure/ConfigProcedureStore.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/ConfigProcedureStore.java index cd061f928522..8a9eb977b77e 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/procedure/ConfigProcedureStore.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/ConfigProcedureStore.java @@ -41,15 +41,14 @@ public class ConfigProcedureStore implements IProcedureStore { private static final Logger LOG = LoggerFactory.getLogger(ProcedureStore.class); private volatile boolean isRunning = false; - private ProcedureInfo procedureInfo = ProcedureInfo.getInstance(); + private final ProcedureInfo procedureInfo; private final String procedureWalDir = CommonDescriptor.getInstance().getConfig().getProcedureWalFolder(); - private ConfigManager configManager; + private final ConfigManager configManager; - public ConfigProcedureStore() {} - - public ConfigProcedureStore(ConfigManager configManager) { + public ConfigProcedureStore(ConfigManager configManager, ProcedureInfo procedureInfo) { this.configManager = configManager; + this.procedureInfo = procedureInfo; try { checkProcWalDir(procedureWalDir); } catch (IOException e) { diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/procedure/DeleteStorageGroupProcedure.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/DeleteStorageGroupProcedure.java index 97923f493c08..c174d9bccfad 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/procedure/DeleteStorageGroupProcedure.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/DeleteStorageGroupProcedure.java @@ -28,7 +28,7 @@ import org.apache.iotdb.commons.utils.TestOnly; import org.apache.iotdb.commons.utils.ThriftConfigNodeSerDeUtils; import org.apache.iotdb.confignode.consensus.request.write.DeleteStorageGroupReq; -import org.apache.iotdb.confignode.persistence.PartitionInfo; +import org.apache.iotdb.confignode.manager.ConfigManager; import org.apache.iotdb.confignode.procedure.env.ConfigNodeProcedureEnv; import org.apache.iotdb.confignode.rpc.thrift.TStorageGroupSchema; import org.apache.iotdb.mpp.rpc.thrift.InternalService; @@ -55,6 +55,8 @@ public class DeleteStorageGroupProcedure private static boolean byPassForTest = false; + private ConfigManager configManager; + @TestOnly public static void setByPassForTest(boolean byPass) { byPassForTest = byPass; @@ -66,9 +68,11 @@ public DeleteStorageGroupProcedure() { super(); } - public DeleteStorageGroupProcedure(TStorageGroupSchema deleteSgSchema) { + public DeleteStorageGroupProcedure( + TStorageGroupSchema deleteSgSchema, ConfigManager configManager) { super(); this.deleteSgSchema = deleteSgSchema; + this.configManager = configManager; } public TStorageGroupSchema getDeleteSgSchema() { @@ -89,12 +93,14 @@ protected Flow executeFromState(ConfigNodeProcedureEnv env, DeleteStorageGroupSt List dataRegionGroupIds = deleteSgSchema.getDataRegionGroupIds(); List schemaRegionGroupIds = deleteSgSchema.getSchemaRegionGroupIds(); List dataRegionReplicaSets = - new ArrayList<>(PartitionInfo.getInstance().getRegionReplicaSets(dataRegionGroupIds)); + new ArrayList<>( + configManager.getPartitionManager().getRegionReplicaSets(dataRegionGroupIds)); List schemaRegionReplicaSets = - new ArrayList<>(PartitionInfo.getInstance().getRegionReplicaSets(schemaRegionGroupIds)); + new ArrayList<>( + configManager.getPartitionManager().getRegionReplicaSets(schemaRegionGroupIds)); try { switch (state) { - case DELETE_STORAGE_GROUP_PREPEARE: + case DELETE_STORAGE_GROUP_PREPARE: // TODO: lock related ClusterSchemaInfo, PartitionInfo and Regions setNextState(DeleteStorageGroupState.DELETE_DATA_REGION); break; @@ -210,7 +216,7 @@ protected int getStateId(DeleteStorageGroupState deleteStorageGroupState) { @Override protected DeleteStorageGroupState getInitialState() { - return DeleteStorageGroupState.DELETE_STORAGE_GROUP_PREPEARE; + return DeleteStorageGroupState.DELETE_STORAGE_GROUP_PREPARE; } @Override diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/procedure/DeleteStorageGroupState.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/DeleteStorageGroupState.java index eecc11578f8c..5e865e2fd11c 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/procedure/DeleteStorageGroupState.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/DeleteStorageGroupState.java @@ -20,7 +20,7 @@ package org.apache.iotdb.confignode.procedure; public enum DeleteStorageGroupState { - DELETE_STORAGE_GROUP_PREPEARE, + DELETE_STORAGE_GROUP_PREPARE, DELETE_DATA_REGION, DELETE_SCHEMA_REGION, DELETE_CONFIG, diff --git a/confignode/src/test/java/org/apache/iotdb/confignode/manager/hash/DeviceGroupHashExecutorManualTest.java b/confignode/src/test/java/org/apache/iotdb/confignode/manager/hash/DeviceGroupHashExecutorManualTest.java index 4f668d407b04..47675f349e76 100644 --- a/confignode/src/test/java/org/apache/iotdb/confignode/manager/hash/DeviceGroupHashExecutorManualTest.java +++ b/confignode/src/test/java/org/apache/iotdb/confignode/manager/hash/DeviceGroupHashExecutorManualTest.java @@ -20,6 +20,7 @@ import org.apache.iotdb.confignode.manager.ConfigManager; import org.apache.iotdb.confignode.manager.PartitionManager; +import org.apache.iotdb.confignode.persistence.PartitionInfo; import java.io.IOException; import java.util.ArrayList; @@ -61,7 +62,7 @@ private List genBatchDevices() { } public void GeneralIndexTest() throws IOException { - PartitionManager manager = new PartitionManager(new ConfigManager()); + PartitionManager manager = new PartitionManager(new ConfigManager(), new PartitionInfo()); int[] bucket = new int[deviceGroupCount]; Arrays.fill(bucket, 0); diff --git a/confignode/src/test/java/org/apache/iotdb/confignode/persistence/AuthorInfoTest.java b/confignode/src/test/java/org/apache/iotdb/confignode/persistence/AuthorInfoTest.java index d1f80ff43a4d..b85987a280f4 100644 --- a/confignode/src/test/java/org/apache/iotdb/confignode/persistence/AuthorInfoTest.java +++ b/confignode/src/test/java/org/apache/iotdb/confignode/persistence/AuthorInfoTest.java @@ -59,7 +59,7 @@ public class AuthorInfoTest { @BeforeClass public static void setup() { - authorInfo = AuthorInfo.getInstance(); + authorInfo = new AuthorInfo(); if (!snapshotDir.exists()) { snapshotDir.mkdirs(); } diff --git a/confignode/src/test/java/org/apache/iotdb/confignode/persistence/ClusterSchemaInfoTest.java b/confignode/src/test/java/org/apache/iotdb/confignode/persistence/ClusterSchemaInfoTest.java index 629d2165975f..0735a83de099 100644 --- a/confignode/src/test/java/org/apache/iotdb/confignode/persistence/ClusterSchemaInfoTest.java +++ b/confignode/src/test/java/org/apache/iotdb/confignode/persistence/ClusterSchemaInfoTest.java @@ -48,7 +48,7 @@ public class ClusterSchemaInfoTest { @BeforeClass public static void setup() { - clusterSchemaInfo = ClusterSchemaInfo.getInstance(); + clusterSchemaInfo = new ClusterSchemaInfo(); if (!snapshotDir.exists()) { snapshotDir.mkdirs(); } diff --git a/confignode/src/test/java/org/apache/iotdb/confignode/persistence/NodeInfoTest.java b/confignode/src/test/java/org/apache/iotdb/confignode/persistence/NodeInfoTest.java index 2caf683a1b35..febddbe2efa1 100644 --- a/confignode/src/test/java/org/apache/iotdb/confignode/persistence/NodeInfoTest.java +++ b/confignode/src/test/java/org/apache/iotdb/confignode/persistence/NodeInfoTest.java @@ -44,7 +44,7 @@ public class NodeInfoTest { @BeforeClass public static void setup() { - nodeInfo = NodeInfo.getInstance(); + nodeInfo = new NodeInfo(); if (!snapshotDir.exists()) { snapshotDir.mkdirs(); } diff --git a/confignode/src/test/java/org/apache/iotdb/confignode/persistence/PartitionInfoTest.java b/confignode/src/test/java/org/apache/iotdb/confignode/persistence/PartitionInfoTest.java index 6e120afd86a8..e6668cd77820 100644 --- a/confignode/src/test/java/org/apache/iotdb/confignode/persistence/PartitionInfoTest.java +++ b/confignode/src/test/java/org/apache/iotdb/confignode/persistence/PartitionInfoTest.java @@ -70,7 +70,7 @@ public int getFlag() { @BeforeClass public static void setup() { - partitionInfo = PartitionInfo.getInstance(); + partitionInfo = new PartitionInfo(); if (!snapshotDir.exists()) { snapshotDir.mkdirs(); } diff --git a/confignode/src/test/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessorTest.java b/confignode/src/test/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessorTest.java index 3853edc0ca1c..d4fb24adeead 100644 --- a/confignode/src/test/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessorTest.java +++ b/confignode/src/test/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessorTest.java @@ -37,9 +37,6 @@ import org.apache.iotdb.confignode.conf.ConfigNodeDescriptor; import org.apache.iotdb.confignode.conf.ConfigNodeStartupCheck; import org.apache.iotdb.confignode.manager.ConfigManager; -import org.apache.iotdb.confignode.persistence.ClusterSchemaInfo; -import org.apache.iotdb.confignode.persistence.NodeInfo; -import org.apache.iotdb.confignode.persistence.PartitionInfo; import org.apache.iotdb.confignode.procedure.DeleteStorageGroupProcedure; import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerReq; import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerResp; @@ -103,10 +100,7 @@ public void before() throws IOException { } @After - public void after() throws IOException { - NodeInfo.getInstance().clear(); - ClusterSchemaInfo.getInstance().clear(); - PartitionInfo.getInstance().clear(); + public void after() throws IOException, InterruptedException { processor.close(); FileUtils.deleteFully(new File(ConfigNodeDescriptor.getInstance().getConf().getConsensusDir())); FileUtils.deleteFully( From fa06bbb43243fef53fa07b82dc848cd3684e09b4 Mon Sep 17 00:00:00 2001 From: cmlmakahts <82880298+cmlmakahts@users.noreply.github.com> Date: Mon, 16 May 2022 14:19:09 +0800 Subject: [PATCH 018/436] [IOTDB-2880] move procedure module into confignode as a class-package (#5899) --- LICENSE | 14 +++ .../request/write/UpdateProcedureReq.java | 4 +- .../confignode/manager/ProcedureManager.java | 16 +-- .../confignode/persistence/ProcedureInfo.java | 8 +- .../CompletedProcedureContainer.java | 6 +- .../procedure/CompletedProcedureRecycler.java | 20 +-- .../procedure/InternalProcedure.java | 6 +- .../confignode}/procedure/Procedure.java | 66 +++++----- .../procedure/ProcedureExecutor.java | 75 ++++++----- .../procedure/RootProcedureStack.java | 6 +- .../procedure/StateMachineProcedure.java | 10 +- .../procedure/StoppableThread.java | 2 +- .../procedure/TimeoutExecutorThread.java | 2 +- .../procedure/conf/ProcedureNodeConfig.java | 6 +- .../conf/ProcedureNodeConfigDescriptor.java | 2 +- .../procedure/conf/ProcedureNodeConstant.java | 2 +- .../exception/ProcedureAbortedException.java | 2 +- .../exception/ProcedureException.java | 2 +- .../ProcedureSuspendedException.java | 2 +- .../exception/ProcedureTimeoutException.java | 2 +- .../exception/ProcedureYieldException.java | 2 +- .../DeleteStorageGroupProcedure.java | 24 ++-- .../scheduler/AbstractProcedureScheduler.java | 4 +- .../scheduler/ProcedureScheduler.java | 4 +- .../scheduler/SimpleProcedureScheduler.java | 4 +- .../{ => state}/DeleteStorageGroupState.java | 2 +- .../procedure/state}/ProcedureLockState.java | 2 +- .../procedure/state/ProcedureState.java | 12 +- .../{ => store}/ConfigProcedureStore.java | 12 +- .../procedure/store/IProcedureFactory.java | 4 +- .../procedure/store/IProcedureStore.java | 8 +- .../{ => store}/ProcedureFactory.java | 11 +- .../procedure/store/ProcedureStore.java | 8 +- .../procedure/store/ProcedureWAL.java | 4 +- .../request/ConfigRequestSerDeTest.java | 4 +- .../procedure/NoopProcedureStore.java | 8 +- .../confignode}/procedure/TestLockRegime.java | 6 +- .../confignode}/procedure/TestProcEnv.java | 4 +- .../procedure/TestProcedureBase.java | 11 +- .../procedure/TestProcedureExecutor.java | 10 +- .../procedure/TestSTMProcedure.java | 6 +- .../procedure/entity/IncProcedure.java | 10 +- .../procedure/entity/NoopProcedure.java | 10 +- .../procedure/entity/SimpleLockProcedure.java | 14 +-- .../procedure/entity/SimpleSTMProcedure.java | 12 +- .../procedure/entity/SleepProcedure.java | 12 +- .../procedure/entity/StuckProcedure.java | 6 +- .../procedure/entity/StuckSTMProcedure.java | 12 +- .../entity/TestProcedureFactory.java | 6 +- .../procedure/store/TestProcedureStore.java | 29 +++-- .../procedure/util/ProcedureTestUtil.java | 8 +- .../ConfigNodeRPCServiceProcessorTest.java | 2 +- .../iotdb/commons/concurrent/ThreadName.java | 4 +- .../iotdb/commons/service/ServiceType.java | 3 +- pom.xml | 2 - procedure/pom.xml | 93 -------------- .../env/ClusterProcedureEnvironment.java | 25 ---- .../procedure/service/ProcedureNode.java | 98 --------------- .../procedure/service/ProcedureServer.java | 117 ------------------ .../service/ProcedureServerCommandLine.java | 52 -------- .../service/ProcedureServerProcessor.java | 87 ------------- .../service/ProcedureServiceHanlder.java | 45 ------- .../service/TestProcedureService.java | 89 ------------- thrift-procedure/pom.xml | 66 ---------- .../src/main/thrift/procedure.thrift | 42 ------- 65 files changed, 293 insertions(+), 954 deletions(-) rename procedure/src/main/java/org/apache/iotdb/procedure/CompletedProcedureRetainer.java => confignode/src/main/java/org/apache/iotdb/confignode/procedure/CompletedProcedureContainer.java (87%) rename procedure/src/main/java/org/apache/iotdb/procedure/CompletedProcedureCleaner.java => confignode/src/main/java/org/apache/iotdb/confignode/procedure/CompletedProcedureRecycler.java (80%) rename {procedure/src/main/java/org/apache/iotdb => confignode/src/main/java/org/apache/iotdb/confignode}/procedure/InternalProcedure.java (89%) rename {procedure/src/main/java/org/apache/iotdb => confignode/src/main/java/org/apache/iotdb/confignode}/procedure/Procedure.java (90%) rename {procedure/src/main/java/org/apache/iotdb => confignode/src/main/java/org/apache/iotdb/confignode}/procedure/ProcedureExecutor.java (90%) rename {procedure/src/main/java/org/apache/iotdb => confignode/src/main/java/org/apache/iotdb/confignode}/procedure/RootProcedureStack.java (96%) rename {procedure/src/main/java/org/apache/iotdb => confignode/src/main/java/org/apache/iotdb/confignode}/procedure/StateMachineProcedure.java (95%) rename {procedure/src/main/java/org/apache/iotdb => confignode/src/main/java/org/apache/iotdb/confignode}/procedure/StoppableThread.java (97%) rename {procedure/src/main/java/org/apache/iotdb => confignode/src/main/java/org/apache/iotdb/confignode}/procedure/TimeoutExecutorThread.java (98%) rename {procedure/src/main/java/org/apache/iotdb => confignode/src/main/java/org/apache/iotdb/confignode}/procedure/conf/ProcedureNodeConfig.java (95%) rename {procedure/src/main/java/org/apache/iotdb => confignode/src/main/java/org/apache/iotdb/confignode}/procedure/conf/ProcedureNodeConfigDescriptor.java (99%) rename {procedure/src/main/java/org/apache/iotdb => confignode/src/main/java/org/apache/iotdb/confignode}/procedure/conf/ProcedureNodeConstant.java (96%) rename {procedure/src/main/java/org/apache/iotdb => confignode/src/main/java/org/apache/iotdb/confignode}/procedure/exception/ProcedureAbortedException.java (94%) rename {procedure/src/main/java/org/apache/iotdb => confignode/src/main/java/org/apache/iotdb/confignode}/procedure/exception/ProcedureException.java (95%) rename {procedure/src/main/java/org/apache/iotdb => confignode/src/main/java/org/apache/iotdb/confignode}/procedure/exception/ProcedureSuspendedException.java (95%) rename {procedure/src/main/java/org/apache/iotdb => confignode/src/main/java/org/apache/iotdb/confignode}/procedure/exception/ProcedureTimeoutException.java (94%) rename {procedure/src/main/java/org/apache/iotdb => confignode/src/main/java/org/apache/iotdb/confignode}/procedure/exception/ProcedureYieldException.java (95%) rename confignode/src/main/java/org/apache/iotdb/confignode/procedure/{ => impl}/DeleteStorageGroupProcedure.java (90%) rename {procedure/src/main/java/org/apache/iotdb => confignode/src/main/java/org/apache/iotdb/confignode}/procedure/scheduler/AbstractProcedureScheduler.java (98%) rename {procedure/src/main/java/org/apache/iotdb => confignode/src/main/java/org/apache/iotdb/confignode}/procedure/scheduler/ProcedureScheduler.java (96%) rename {procedure/src/main/java/org/apache/iotdb => confignode/src/main/java/org/apache/iotdb/confignode}/procedure/scheduler/SimpleProcedureScheduler.java (94%) rename confignode/src/main/java/org/apache/iotdb/confignode/procedure/{ => state}/DeleteStorageGroupState.java (94%) rename {procedure/src/main/java/org/apache/iotdb/procedure => confignode/src/main/java/org/apache/iotdb/confignode/procedure/state}/ProcedureLockState.java (94%) rename procedure/src/main/java/org/apache/iotdb/procedure/service/ProcedureNodeMBean.java => confignode/src/main/java/org/apache/iotdb/confignode/procedure/state/ProcedureState.java (82%) rename confignode/src/main/java/org/apache/iotdb/confignode/procedure/{ => store}/ConfigProcedureStore.java (91%) rename {procedure/src/main/java/org/apache/iotdb => confignode/src/main/java/org/apache/iotdb/confignode}/procedure/store/IProcedureFactory.java (89%) rename {procedure/src/main/java/org/apache/iotdb => confignode/src/main/java/org/apache/iotdb/confignode}/procedure/store/IProcedureStore.java (81%) rename confignode/src/main/java/org/apache/iotdb/confignode/procedure/{ => store}/ProcedureFactory.java (84%) rename {procedure/src/main/java/org/apache/iotdb => confignode/src/main/java/org/apache/iotdb/confignode}/procedure/store/ProcedureStore.java (95%) rename {procedure/src/main/java/org/apache/iotdb => confignode/src/main/java/org/apache/iotdb/confignode}/procedure/store/ProcedureWAL.java (96%) rename {procedure/src/test/java/org/apache/iotdb => confignode/src/test/java/org/apache/iotdb/confignode}/procedure/NoopProcedureStore.java (84%) rename {procedure/src/test/java/org/apache/iotdb => confignode/src/test/java/org/apache/iotdb/confignode}/procedure/TestLockRegime.java (89%) rename {procedure/src/test/java/org/apache/iotdb => confignode/src/test/java/org/apache/iotdb/confignode}/procedure/TestProcEnv.java (93%) rename {procedure/src/test/java/org/apache/iotdb => confignode/src/test/java/org/apache/iotdb/confignode}/procedure/TestProcedureBase.java (82%) rename {procedure/src/test/java/org/apache/iotdb => confignode/src/test/java/org/apache/iotdb/confignode}/procedure/TestProcedureExecutor.java (92%) rename {procedure/src/test/java/org/apache/iotdb => confignode/src/test/java/org/apache/iotdb/confignode}/procedure/TestSTMProcedure.java (91%) rename {procedure/src/test/java/org/apache/iotdb => confignode/src/test/java/org/apache/iotdb/confignode}/procedure/entity/IncProcedure.java (85%) rename {procedure/src/test/java/org/apache/iotdb => confignode/src/test/java/org/apache/iotdb/confignode}/procedure/entity/NoopProcedure.java (79%) rename {procedure/src/test/java/org/apache/iotdb => confignode/src/test/java/org/apache/iotdb/confignode}/procedure/entity/SimpleLockProcedure.java (83%) rename {procedure/src/test/java/org/apache/iotdb => confignode/src/test/java/org/apache/iotdb/confignode}/procedure/entity/SimpleSTMProcedure.java (86%) rename {procedure/src/test/java/org/apache/iotdb => confignode/src/test/java/org/apache/iotdb/confignode}/procedure/entity/SleepProcedure.java (80%) rename {procedure/src/test/java/org/apache/iotdb => confignode/src/test/java/org/apache/iotdb/confignode}/procedure/entity/StuckProcedure.java (91%) rename {procedure/src/test/java/org/apache/iotdb => confignode/src/test/java/org/apache/iotdb/confignode}/procedure/entity/StuckSTMProcedure.java (87%) rename {procedure/src/test/java/org/apache/iotdb => confignode/src/test/java/org/apache/iotdb/confignode}/procedure/entity/TestProcedureFactory.java (92%) rename {procedure/src/test/java/org/apache/iotdb => confignode/src/test/java/org/apache/iotdb/confignode}/procedure/store/TestProcedureStore.java (76%) rename {procedure/src/test/java/org/apache/iotdb => confignode/src/test/java/org/apache/iotdb/confignode}/procedure/util/ProcedureTestUtil.java (88%) delete mode 100644 procedure/pom.xml delete mode 100644 procedure/src/main/java/org/apache/iotdb/procedure/env/ClusterProcedureEnvironment.java delete mode 100644 procedure/src/main/java/org/apache/iotdb/procedure/service/ProcedureNode.java delete mode 100644 procedure/src/main/java/org/apache/iotdb/procedure/service/ProcedureServer.java delete mode 100644 procedure/src/main/java/org/apache/iotdb/procedure/service/ProcedureServerCommandLine.java delete mode 100644 procedure/src/main/java/org/apache/iotdb/procedure/service/ProcedureServerProcessor.java delete mode 100644 procedure/src/main/java/org/apache/iotdb/procedure/service/ProcedureServiceHanlder.java delete mode 100644 procedure/src/test/java/org/apache/iotdb/procedure/service/TestProcedureService.java delete mode 100644 thrift-procedure/pom.xml delete mode 100644 thrift-procedure/src/main/thrift/procedure.thrift diff --git a/LICENSE b/LICENSE index 2cabd6a47c68..f60a85675f81 100644 --- a/LICENSE +++ b/LICENSE @@ -237,6 +237,20 @@ License: http://www.apache.org/licenses/LICENSE-2.0 -------------------------------------------------------------------------------- +The following files include code modified from Apache HBase project. + +./confignode/src/main/java/org/apache/iotdb/procedure/Procedure.java +./confignode/src/main/java/org/apache/iotdb/procedure/ProcedureExecutor.java +./confignode/src/main/java/org/apache/iotdb/procedure/StateMachineProcedure.java +./confignode/src/main/java/org/apache/iotdb/procedure/TimeoutExecutorThread.java +./confignode/src/main/java/org/apache/iotdb/procedure/StoppableThread.java + +Copyright: 2016-2018 Michael Burman and/or other contributors +Project page: https://github.com/burmanm/gorilla-tsc +License: http://www.apache.org/licenses/LICENSE-2.0 + +-------------------------------------------------------------------------------- + The following files include code modified from Eclipse Collections project. ./tsfile/src/main/java/org/apache/iotdb/tsfile/utils/ByteArrayList.java diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/UpdateProcedureReq.java b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/UpdateProcedureReq.java index ba2e47cf1ca2..2e99380bc2e2 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/UpdateProcedureReq.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/UpdateProcedureReq.java @@ -21,8 +21,8 @@ import org.apache.iotdb.confignode.consensus.request.ConfigRequest; import org.apache.iotdb.confignode.consensus.request.ConfigRequestType; -import org.apache.iotdb.confignode.procedure.ProcedureFactory; -import org.apache.iotdb.procedure.Procedure; +import org.apache.iotdb.confignode.procedure.Procedure; +import org.apache.iotdb.confignode.procedure.store.ProcedureFactory; import java.io.IOException; import java.nio.ByteBuffer; diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/ProcedureManager.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/ProcedureManager.java index f433e6923408..45f8d8dd5e8c 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/ProcedureManager.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/ProcedureManager.java @@ -24,16 +24,16 @@ import org.apache.iotdb.confignode.conf.ConfigNodeConf; import org.apache.iotdb.confignode.conf.ConfigNodeDescriptor; import org.apache.iotdb.confignode.persistence.ProcedureInfo; -import org.apache.iotdb.confignode.procedure.ConfigProcedureStore; -import org.apache.iotdb.confignode.procedure.DeleteStorageGroupProcedure; +import org.apache.iotdb.confignode.procedure.Procedure; +import org.apache.iotdb.confignode.procedure.ProcedureExecutor; import org.apache.iotdb.confignode.procedure.env.ConfigNodeProcedureEnv; +import org.apache.iotdb.confignode.procedure.impl.DeleteStorageGroupProcedure; +import org.apache.iotdb.confignode.procedure.scheduler.ProcedureScheduler; +import org.apache.iotdb.confignode.procedure.scheduler.SimpleProcedureScheduler; +import org.apache.iotdb.confignode.procedure.store.ConfigProcedureStore; +import org.apache.iotdb.confignode.procedure.store.IProcedureStore; +import org.apache.iotdb.confignode.procedure.store.ProcedureStore; import org.apache.iotdb.confignode.rpc.thrift.TStorageGroupSchema; -import org.apache.iotdb.procedure.Procedure; -import org.apache.iotdb.procedure.ProcedureExecutor; -import org.apache.iotdb.procedure.scheduler.ProcedureScheduler; -import org.apache.iotdb.procedure.scheduler.SimpleProcedureScheduler; -import org.apache.iotdb.procedure.store.IProcedureStore; -import org.apache.iotdb.procedure.store.ProcedureStore; import org.apache.iotdb.rpc.RpcUtils; import org.slf4j.Logger; diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/ProcedureInfo.java b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/ProcedureInfo.java index 1a0561a5513d..62db45719d90 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/ProcedureInfo.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/ProcedureInfo.java @@ -23,10 +23,10 @@ import org.apache.iotdb.commons.conf.CommonDescriptor; import org.apache.iotdb.confignode.consensus.request.write.DeleteProcedureReq; import org.apache.iotdb.confignode.consensus.request.write.UpdateProcedureReq; -import org.apache.iotdb.confignode.procedure.ProcedureFactory; -import org.apache.iotdb.procedure.Procedure; -import org.apache.iotdb.procedure.conf.ProcedureNodeConstant; -import org.apache.iotdb.procedure.store.ProcedureWAL; +import org.apache.iotdb.confignode.procedure.Procedure; +import org.apache.iotdb.confignode.procedure.conf.ProcedureNodeConstant; +import org.apache.iotdb.confignode.procedure.store.ProcedureFactory; +import org.apache.iotdb.confignode.procedure.store.ProcedureWAL; import org.apache.iotdb.rpc.TSStatusCode; import org.slf4j.Logger; diff --git a/procedure/src/main/java/org/apache/iotdb/procedure/CompletedProcedureRetainer.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/CompletedProcedureContainer.java similarity index 87% rename from procedure/src/main/java/org/apache/iotdb/procedure/CompletedProcedureRetainer.java rename to confignode/src/main/java/org/apache/iotdb/confignode/procedure/CompletedProcedureContainer.java index 812173700181..ba6712a983d3 100644 --- a/procedure/src/main/java/org/apache/iotdb/procedure/CompletedProcedureRetainer.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/CompletedProcedureContainer.java @@ -17,12 +17,12 @@ * under the License. */ -package org.apache.iotdb.procedure; +package org.apache.iotdb.confignode.procedure; -public class CompletedProcedureRetainer { +public class CompletedProcedureContainer { private final Procedure procedure; - public CompletedProcedureRetainer(Procedure procedure) { + public CompletedProcedureContainer(Procedure procedure) { this.procedure = procedure; } diff --git a/procedure/src/main/java/org/apache/iotdb/procedure/CompletedProcedureCleaner.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/CompletedProcedureRecycler.java similarity index 80% rename from procedure/src/main/java/org/apache/iotdb/procedure/CompletedProcedureCleaner.java rename to confignode/src/main/java/org/apache/iotdb/confignode/procedure/CompletedProcedureRecycler.java index ffc2c3816b12..66af46fa9a38 100644 --- a/procedure/src/main/java/org/apache/iotdb/procedure/CompletedProcedureCleaner.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/CompletedProcedureRecycler.java @@ -17,9 +17,9 @@ * under the License. */ -package org.apache.iotdb.procedure; +package org.apache.iotdb.confignode.procedure; -import org.apache.iotdb.procedure.store.IProcedureStore; +import org.apache.iotdb.confignode.procedure.store.IProcedureStore; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,16 +29,16 @@ import java.util.concurrent.TimeUnit; /** Internal cleaner that removes the completed procedure results after a TTL. */ -public class CompletedProcedureCleaner extends InternalProcedure { - private static final Logger LOG = LoggerFactory.getLogger(CompletedProcedureCleaner.class); +public class CompletedProcedureRecycler extends InternalProcedure { + private static final Logger LOG = LoggerFactory.getLogger(CompletedProcedureRecycler.class); private static final int DEFAULT_BATCH_SIZE = 32; private long evictTTL; - private final Map> completed; + private final Map> completed; private final IProcedureStore store; - public CompletedProcedureCleaner( + public CompletedProcedureRecycler( IProcedureStore store, - Map> completedMap, + Map> completedMap, long cleanTimeInterval, long evictTTL) { super(TimeUnit.SECONDS.toMillis(cleanTimeInterval)); @@ -60,11 +60,11 @@ protected void periodicExecute(final Env env) { int batchCount = 0; final long now = System.currentTimeMillis(); - final Iterator>> it = + final Iterator>> it = completed.entrySet().iterator(); while (it.hasNext() && store.isRunning()) { - final Map.Entry> entry = it.next(); - final CompletedProcedureRetainer retainer = entry.getValue(); + final Map.Entry> entry = it.next(); + final CompletedProcedureContainer retainer = entry.getValue(); final Procedure proc = retainer.getProcedure(); if (retainer.isExpired(now, evictTTL)) { // Failed procedures aren't persisted in WAL. diff --git a/procedure/src/main/java/org/apache/iotdb/procedure/InternalProcedure.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/InternalProcedure.java similarity index 89% rename from procedure/src/main/java/org/apache/iotdb/procedure/InternalProcedure.java rename to confignode/src/main/java/org/apache/iotdb/confignode/procedure/InternalProcedure.java index 6d76e5e79db8..91922f9f7c9b 100644 --- a/procedure/src/main/java/org/apache/iotdb/procedure/InternalProcedure.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/InternalProcedure.java @@ -17,10 +17,10 @@ * under the License. */ -package org.apache.iotdb.procedure; +package org.apache.iotdb.confignode.procedure; -import org.apache.iotdb.procedure.exception.ProcedureSuspendedException; -import org.apache.iotdb.procedure.exception.ProcedureYieldException; +import org.apache.iotdb.confignode.procedure.exception.ProcedureSuspendedException; +import org.apache.iotdb.confignode.procedure.exception.ProcedureYieldException; import java.io.IOException; import java.nio.ByteBuffer; diff --git a/procedure/src/main/java/org/apache/iotdb/procedure/Procedure.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/Procedure.java similarity index 90% rename from procedure/src/main/java/org/apache/iotdb/procedure/Procedure.java rename to confignode/src/main/java/org/apache/iotdb/confignode/procedure/Procedure.java index 6ca34b3a1f0b..616bbe90ac5f 100644 --- a/procedure/src/main/java/org/apache/iotdb/procedure/Procedure.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/Procedure.java @@ -17,15 +17,16 @@ * under the License. */ -package org.apache.iotdb.procedure; +package org.apache.iotdb.confignode.procedure; -import org.apache.iotdb.procedure.exception.ProcedureAbortedException; -import org.apache.iotdb.procedure.exception.ProcedureException; -import org.apache.iotdb.procedure.exception.ProcedureSuspendedException; -import org.apache.iotdb.procedure.exception.ProcedureTimeoutException; -import org.apache.iotdb.procedure.exception.ProcedureYieldException; -import org.apache.iotdb.procedure.store.IProcedureStore; -import org.apache.iotdb.service.rpc.thrift.ProcedureState; +import org.apache.iotdb.confignode.procedure.exception.ProcedureAbortedException; +import org.apache.iotdb.confignode.procedure.exception.ProcedureException; +import org.apache.iotdb.confignode.procedure.exception.ProcedureSuspendedException; +import org.apache.iotdb.confignode.procedure.exception.ProcedureTimeoutException; +import org.apache.iotdb.confignode.procedure.exception.ProcedureYieldException; +import org.apache.iotdb.confignode.procedure.state.ProcedureLockState; +import org.apache.iotdb.confignode.procedure.state.ProcedureState; +import org.apache.iotdb.confignode.procedure.store.IProcedureStore; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -56,7 +57,7 @@ public abstract class Procedure implements Comparable> { private ProcedureState state = ProcedureState.INITIALIZING; private int childrenLatch = 0; - private ProcedureException exception; + private org.apache.iotdb.confignode.procedure.exception.ProcedureException exception; private volatile long timeout = NO_TIMEOUT; private volatile long lastUpdate; @@ -94,14 +95,17 @@ public final boolean hasLock() { * @param env the environment passed to the ProcedureExecutor * @return a set of sub-procedures to run or ourselves if there is more work to do or null if the * procedure is done. - * @throws ProcedureYieldException the procedure will be added back to the queue and retried - * later. + * @throws org.apache.iotdb.confignode.procedure.exception.ProcedureYieldException the procedure + * will be added back to the queue and retried later. * @throws InterruptedException the procedure will be added back to the queue and retried later. - * @throws ProcedureSuspendedException Signal to the executor that Procedure has suspended itself - * and has set itself up waiting for an external event to wake it back up again. + * @throws org.apache.iotdb.confignode.procedure.exception.ProcedureSuspendedException Signal to + * the executor that Procedure has suspended itself and has set itself up waiting for an + * external event to wake it back up again. */ protected abstract Procedure[] execute(Env env) - throws ProcedureYieldException, ProcedureSuspendedException, InterruptedException; + throws org.apache.iotdb.confignode.procedure.exception.ProcedureYieldException, + org.apache.iotdb.confignode.procedure.exception.ProcedureSuspendedException, + InterruptedException; /** * The code to undo what was done by the execute() code. It is called when the procedure or one of @@ -130,7 +134,7 @@ public void serialize(ByteBuffer byteBuffer) { // procid byteBuffer.putLong(this.procId); // state - byteBuffer.putInt(this.state.getValue()); + byteBuffer.putInt(this.state.ordinal()); // submit time byteBuffer.putLong(this.submittedTime); // last updated @@ -182,7 +186,7 @@ public void deserialize(ByteBuffer byteBuffer) { // procid this.setProcId(byteBuffer.getLong()); // state - this.setState(ProcedureState.findByValue(byteBuffer.getInt())); + this.setState(ProcedureState.values()[byteBuffer.getInt()]); // submit time this.setSubmittedTime(byteBuffer.getLong()); // last updated @@ -210,16 +214,17 @@ public void deserialize(ByteBuffer byteBuffer) { byteBuffer.get(messageBytes); errMsg = new String(messageBytes, StandardCharsets.UTF_8); } - ProcedureException exception; + org.apache.iotdb.confignode.procedure.exception.ProcedureException exception; try { exception = - (ProcedureException) exceptionClass.getConstructor(String.class).newInstance(errMsg); + (org.apache.iotdb.confignode.procedure.exception.ProcedureException) + exceptionClass.getConstructor(String.class).newInstance(errMsg); } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { LOG.warn("Instantiation exception class failed", e); - exception = new ProcedureException(errMsg); + exception = new org.apache.iotdb.confignode.procedure.exception.ProcedureException(errMsg); } setFailure(exception); @@ -285,8 +290,8 @@ protected boolean waitInitialized(Env env) { * @param env environment * @return state of lock */ - protected ProcedureLockState acquireLock(Env env) { - return ProcedureLockState.LOCK_ACQUIRED; + protected org.apache.iotdb.confignode.procedure.state.ProcedureLockState acquireLock(Env env) { + return org.apache.iotdb.confignode.procedure.state.ProcedureLockState.LOCK_ACQUIRED; } /** @@ -354,7 +359,9 @@ protected boolean isYieldAfterExecution(Env env) { * @return sub procedures */ protected Procedure[] doExecute(Env env) - throws ProcedureYieldException, ProcedureSuspendedException, InterruptedException { + throws ProcedureYieldException, + org.apache.iotdb.confignode.procedure.exception.ProcedureSuspendedException, + InterruptedException { try { updateTimestamp(); return execute(env); @@ -386,16 +393,17 @@ public void doRollback(Env env) throws IOException, InterruptedException { * @param store ProcedureStore * @return ProcedureLockState */ - public final ProcedureLockState doAcquireLock(Env env, IProcedureStore store) { + public final org.apache.iotdb.confignode.procedure.state.ProcedureLockState doAcquireLock( + Env env, IProcedureStore store) { if (waitInitialized(env)) { - return ProcedureLockState.LOCK_EVENT_WAIT; + return org.apache.iotdb.confignode.procedure.state.ProcedureLockState.LOCK_EVENT_WAIT; } if (lockedWhenLoading) { lockedWhenLoading = false; locked = true; - return ProcedureLockState.LOCK_ACQUIRED; + return org.apache.iotdb.confignode.procedure.state.ProcedureLockState.LOCK_ACQUIRED; } - ProcedureLockState state = acquireLock(env); + org.apache.iotdb.confignode.procedure.state.ProcedureLockState state = acquireLock(env); if (state == ProcedureLockState.LOCK_ACQUIRED) { locked = true; store.update(this); @@ -728,10 +736,12 @@ public synchronized ProcedureState getState() { } protected void setFailure(final String source, final Throwable cause) { - setFailure(new ProcedureException(source, cause)); + setFailure( + new org.apache.iotdb.confignode.procedure.exception.ProcedureException(source, cause)); } - protected synchronized void setFailure(final ProcedureException exception) { + protected synchronized void setFailure( + final org.apache.iotdb.confignode.procedure.exception.ProcedureException exception) { this.exception = exception; if (!isFinished()) { setState(ProcedureState.FAILED); diff --git a/procedure/src/main/java/org/apache/iotdb/procedure/ProcedureExecutor.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/ProcedureExecutor.java similarity index 90% rename from procedure/src/main/java/org/apache/iotdb/procedure/ProcedureExecutor.java rename to confignode/src/main/java/org/apache/iotdb/confignode/procedure/ProcedureExecutor.java index 35da844070b9..2b23bcedf6bf 100644 --- a/procedure/src/main/java/org/apache/iotdb/procedure/ProcedureExecutor.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/ProcedureExecutor.java @@ -17,15 +17,16 @@ * under the License. */ -package org.apache.iotdb.procedure; +package org.apache.iotdb.confignode.procedure; -import org.apache.iotdb.procedure.exception.ProcedureException; -import org.apache.iotdb.procedure.exception.ProcedureSuspendedException; -import org.apache.iotdb.procedure.exception.ProcedureYieldException; -import org.apache.iotdb.procedure.scheduler.ProcedureScheduler; -import org.apache.iotdb.procedure.scheduler.SimpleProcedureScheduler; -import org.apache.iotdb.procedure.store.IProcedureStore; -import org.apache.iotdb.service.rpc.thrift.ProcedureState; +import org.apache.iotdb.confignode.procedure.exception.ProcedureException; +import org.apache.iotdb.confignode.procedure.exception.ProcedureSuspendedException; +import org.apache.iotdb.confignode.procedure.exception.ProcedureYieldException; +import org.apache.iotdb.confignode.procedure.scheduler.ProcedureScheduler; +import org.apache.iotdb.confignode.procedure.scheduler.SimpleProcedureScheduler; +import org.apache.iotdb.confignode.procedure.state.ProcedureLockState; +import org.apache.iotdb.confignode.procedure.state.ProcedureState; +import org.apache.iotdb.confignode.procedure.store.IProcedureStore; import com.google.common.base.Preconditions; import org.slf4j.Logger; @@ -52,7 +53,7 @@ public class ProcedureExecutor { private final ConcurrentHashMap idLockMap = new ConcurrentHashMap<>(); - private final ConcurrentHashMap> completed = + private final ConcurrentHashMap> completed = new ConcurrentHashMap<>(); private final ConcurrentHashMap> rollbackStack = @@ -64,15 +65,15 @@ public class ProcedureExecutor { private CopyOnWriteArrayList workerThreads; - private TimeoutExecutorThread timeoutExecutor; + private org.apache.iotdb.confignode.procedure.TimeoutExecutorThread timeoutExecutor; - private TimeoutExecutorThread workerMonitorExecutor; + private org.apache.iotdb.confignode.procedure.TimeoutExecutorThread workerMonitorExecutor; private int corePoolSize; private int maxPoolSize; private volatile long keepAliveTime; - private final ProcedureScheduler scheduler; + private final org.apache.iotdb.confignode.procedure.scheduler.ProcedureScheduler scheduler; private final AtomicLong lastProcId = new AtomicLong(-1); private final AtomicLong workId = new AtomicLong(0); @@ -82,7 +83,9 @@ public class ProcedureExecutor { private final IProcedureStore store; public ProcedureExecutor( - final Env environment, final IProcedureStore store, final ProcedureScheduler scheduler) { + final Env environment, + final IProcedureStore store, + final org.apache.iotdb.confignode.procedure.scheduler.ProcedureScheduler scheduler) { this.environment = environment; this.scheduler = scheduler; this.store = store; @@ -98,7 +101,8 @@ public void init(int numThreads) { this.maxPoolSize = 10 * numThreads; this.threadGroup = new ThreadGroup("ProcedureWorkerGroup"); this.timeoutExecutor = - new TimeoutExecutorThread<>(this, threadGroup, "ProcedureTimeoutExecutor"); + new org.apache.iotdb.confignode.procedure.TimeoutExecutorThread<>( + this, threadGroup, "ProcedureTimeoutExecutor"); this.workerMonitorExecutor = new TimeoutExecutorThread<>(this, threadGroup, "ProcedureWorkerThreadMonitor"); workId.set(0); @@ -133,7 +137,7 @@ private void recover() { store.load(procedureList); for (Procedure proc : procedureList) { if (proc.isFinished()) { - completed.putIfAbsent(proc.getProcId(), new CompletedProcedureRetainer(proc)); + completed.putIfAbsent(proc.getProcId(), new CompletedProcedureContainer(proc)); } else { if (!proc.hasParent()) { rollbackStack.put(proc.getProcId(), new RootProcedureStack<>()); @@ -283,7 +287,7 @@ public void startWorkers() { public void startCompletedCleaner(long cleanTimeInterval, long cleanEvictTTL) { addInternalProcedure( - new CompletedProcedureCleaner(store, completed, cleanTimeInterval, cleanEvictTTL)); + new CompletedProcedureRecycler(store, completed, cleanTimeInterval, cleanEvictTTL)); } private void addInternalProcedure(InternalProcedure interalProcedure) { @@ -379,7 +383,7 @@ private void executeProcedure(Procedure proc) { } break; } - ProcedureLockState lockState = acquireLock(proc); + org.apache.iotdb.confignode.procedure.state.ProcedureLockState lockState = acquireLock(proc); switch (lockState) { case LOCK_ACQUIRED: executeProcedure(rootProcStack, proc); @@ -438,7 +442,9 @@ private void executeProcedure(RootProcedureStack rootProcStack, Procedure p yieldProcedure(proc); } catch (Throwable e) { LOG.error("CODE-BUG:{}", proc, e); - proc.setFailure(new ProcedureException(e.getMessage(), e)); + proc.setFailure( + new org.apache.iotdb.confignode.procedure.exception.ProcedureException( + e.getMessage(), e)); } if (!proc.isFailed()) { @@ -549,7 +555,9 @@ private Procedure[] initializeChildren( Procedure subproc = subprocs[i]; if (subproc == null) { String errMsg = "subproc[" + i + "] is null, aborting procedure"; - proc.setFailure(new ProcedureException((errMsg), new IllegalArgumentException(errMsg))); + proc.setFailure( + new org.apache.iotdb.confignode.procedure.exception.ProcedureException( + (errMsg), new IllegalArgumentException(errMsg))); return null; } subproc.setParentProcId(proc.getProcId()); @@ -605,21 +613,24 @@ private ProcedureLockState executeRootStackRollback( cleanupAfterRollback(procedure); continue; } - ProcedureLockState lockState = acquireLock(procedure); - if (lockState != ProcedureLockState.LOCK_ACQUIRED) { + org.apache.iotdb.confignode.procedure.state.ProcedureLockState lockState = + acquireLock(procedure); + if (lockState + != org.apache.iotdb.confignode.procedure.state.ProcedureLockState.LOCK_ACQUIRED) { return lockState; } lockState = executeRollback(procedure); releaseLock(procedure, false); - boolean abortRollback = lockState != ProcedureLockState.LOCK_ACQUIRED; + boolean abortRollback = + lockState != org.apache.iotdb.confignode.procedure.state.ProcedureLockState.LOCK_ACQUIRED; abortRollback |= !isRunning() || !store.isRunning(); if (abortRollback) { return lockState; } if (!procedure.isFinished() && procedure.isYieldAfterExecution(this.environment)) { - return ProcedureLockState.LOCK_YIELD_WAIT; + return org.apache.iotdb.confignode.procedure.state.ProcedureLockState.LOCK_YIELD_WAIT; } if (procedure != rootProcedure) { @@ -629,12 +640,13 @@ private ProcedureLockState executeRootStackRollback( LOG.info("Rolled back {}, time duration is {}", rootProcedure, rootProcedure.elapsedTime()); rootProcedureCleanup(rootProcedure); - return ProcedureLockState.LOCK_ACQUIRED; + return org.apache.iotdb.confignode.procedure.state.ProcedureLockState.LOCK_ACQUIRED; } - private ProcedureLockState acquireLock(Procedure proc) { + private org.apache.iotdb.confignode.procedure.state.ProcedureLockState acquireLock( + Procedure proc) { if (proc.hasLock()) { - return ProcedureLockState.LOCK_ACQUIRED; + return org.apache.iotdb.confignode.procedure.state.ProcedureLockState.LOCK_ACQUIRED; } return proc.doAcquireLock(this.environment, store); } @@ -646,7 +658,8 @@ private ProcedureLockState acquireLock(Procedure proc) { * @param procedure procedure * @return procedure lock state */ - private ProcedureLockState executeRollback(Procedure procedure) { + private org.apache.iotdb.confignode.procedure.state.ProcedureLockState executeRollback( + Procedure procedure) { ReentrantLock idLock = idLockMap.computeIfAbsent(procedure.getProcId(), procId -> new ReentrantLock()); try { @@ -662,7 +675,7 @@ private ProcedureLockState executeRollback(Procedure procedure) { idLock.unlock(); } cleanupAfterRollback(procedure); - return ProcedureLockState.LOCK_ACQUIRED; + return org.apache.iotdb.confignode.procedure.state.ProcedureLockState.LOCK_ACQUIRED; } private void cleanupAfterRollback(Procedure procedure) { @@ -699,7 +712,7 @@ private void executeCompletionCleanup(Procedure proc) { private void rootProcedureCleanup(Procedure proc) { executeCompletionCleanup(proc); - CompletedProcedureRetainer retainer = new CompletedProcedureRetainer<>(proc); + CompletedProcedureContainer retainer = new CompletedProcedureContainer<>(proc); completed.put(proc.getProcId(), retainer); rollbackStack.remove(proc.getProcId()); procedures.remove(proc.getProcId()); @@ -954,7 +967,7 @@ public boolean abort(long procId) { } public Procedure getResult(long procId) { - CompletedProcedureRetainer retainer = completed.get(procId); + CompletedProcedureContainer retainer = completed.get(procId); if (retainer == null) { return null; } else { @@ -969,7 +982,7 @@ public Procedure getResult(long procId) { * @return procedure or retainer */ public Procedure getResultOrProcedure(long procId) { - CompletedProcedureRetainer retainer = completed.get(procId); + CompletedProcedureContainer retainer = completed.get(procId); if (retainer == null) { return procedures.get(procId); } else { diff --git a/procedure/src/main/java/org/apache/iotdb/procedure/RootProcedureStack.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/RootProcedureStack.java similarity index 96% rename from procedure/src/main/java/org/apache/iotdb/procedure/RootProcedureStack.java rename to confignode/src/main/java/org/apache/iotdb/confignode/procedure/RootProcedureStack.java index 2491e827d358..88057c97de5b 100644 --- a/procedure/src/main/java/org/apache/iotdb/procedure/RootProcedureStack.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/RootProcedureStack.java @@ -17,10 +17,10 @@ * under the License. */ -package org.apache.iotdb.procedure; +package org.apache.iotdb.confignode.procedure; -import org.apache.iotdb.procedure.exception.ProcedureException; -import org.apache.iotdb.service.rpc.thrift.ProcedureState; +import org.apache.iotdb.confignode.procedure.exception.ProcedureException; +import org.apache.iotdb.confignode.procedure.state.ProcedureState; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/procedure/src/main/java/org/apache/iotdb/procedure/StateMachineProcedure.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/StateMachineProcedure.java similarity index 95% rename from procedure/src/main/java/org/apache/iotdb/procedure/StateMachineProcedure.java rename to confignode/src/main/java/org/apache/iotdb/confignode/procedure/StateMachineProcedure.java index cf3c812ac47a..6d05b9f73326 100644 --- a/procedure/src/main/java/org/apache/iotdb/procedure/StateMachineProcedure.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/StateMachineProcedure.java @@ -17,10 +17,10 @@ * under the License. */ -package org.apache.iotdb.procedure; +package org.apache.iotdb.confignode.procedure; -import org.apache.iotdb.procedure.exception.ProcedureSuspendedException; -import org.apache.iotdb.procedure.exception.ProcedureYieldException; +import org.apache.iotdb.confignode.procedure.exception.ProcedureSuspendedException; +import org.apache.iotdb.confignode.procedure.exception.ProcedureYieldException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -78,7 +78,9 @@ public enum Flow { * another step. */ protected abstract Flow executeFromState(Env env, TState state) - throws ProcedureSuspendedException, ProcedureYieldException, InterruptedException; + throws org.apache.iotdb.confignode.procedure.exception.ProcedureSuspendedException, + org.apache.iotdb.confignode.procedure.exception.ProcedureYieldException, + InterruptedException; /** * called to perform the rollback of the specified state diff --git a/procedure/src/main/java/org/apache/iotdb/procedure/StoppableThread.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/StoppableThread.java similarity index 97% rename from procedure/src/main/java/org/apache/iotdb/procedure/StoppableThread.java rename to confignode/src/main/java/org/apache/iotdb/confignode/procedure/StoppableThread.java index 9309333a6569..84f2f27ed171 100644 --- a/procedure/src/main/java/org/apache/iotdb/procedure/StoppableThread.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/StoppableThread.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.procedure; +package org.apache.iotdb.confignode.procedure; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/procedure/src/main/java/org/apache/iotdb/procedure/TimeoutExecutorThread.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/TimeoutExecutorThread.java similarity index 98% rename from procedure/src/main/java/org/apache/iotdb/procedure/TimeoutExecutorThread.java rename to confignode/src/main/java/org/apache/iotdb/confignode/procedure/TimeoutExecutorThread.java index 5ac861560ffe..28ba7aafceff 100644 --- a/procedure/src/main/java/org/apache/iotdb/procedure/TimeoutExecutorThread.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/TimeoutExecutorThread.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.procedure; +package org.apache.iotdb.confignode.procedure; import java.util.concurrent.DelayQueue; import java.util.concurrent.Delayed; diff --git a/procedure/src/main/java/org/apache/iotdb/procedure/conf/ProcedureNodeConfig.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/conf/ProcedureNodeConfig.java similarity index 95% rename from procedure/src/main/java/org/apache/iotdb/procedure/conf/ProcedureNodeConfig.java rename to confignode/src/main/java/org/apache/iotdb/confignode/procedure/conf/ProcedureNodeConfig.java index 048f7b0e175e..4b26873500e7 100644 --- a/procedure/src/main/java/org/apache/iotdb/procedure/conf/ProcedureNodeConfig.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/conf/ProcedureNodeConfig.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.procedure.conf; +package org.apache.iotdb.confignode.procedure.conf; import java.io.File; @@ -34,7 +34,9 @@ public class ProcedureNodeConfig { private int thriftDefaultBufferSize = 1024; private int thriftServerAwaitTimeForStopService = 60; private String procedureWalDir = - ProcedureNodeConstant.PROC_DIR + File.separator + ProcedureNodeConstant.WAL_DIR; + org.apache.iotdb.confignode.procedure.conf.ProcedureNodeConstant.PROC_DIR + + File.separator + + ProcedureNodeConstant.WAL_DIR; private int completedEvictTTL = 800; private int completedCleanInterval = 30; private int workerThreadsCoreSize = Math.max(Runtime.getRuntime().availableProcessors() / 4, 16); diff --git a/procedure/src/main/java/org/apache/iotdb/procedure/conf/ProcedureNodeConfigDescriptor.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/conf/ProcedureNodeConfigDescriptor.java similarity index 99% rename from procedure/src/main/java/org/apache/iotdb/procedure/conf/ProcedureNodeConfigDescriptor.java rename to confignode/src/main/java/org/apache/iotdb/confignode/procedure/conf/ProcedureNodeConfigDescriptor.java index cab0bd747318..73ce5651cd1d 100644 --- a/procedure/src/main/java/org/apache/iotdb/procedure/conf/ProcedureNodeConfigDescriptor.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/conf/ProcedureNodeConfigDescriptor.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.procedure.conf; +package org.apache.iotdb.confignode.procedure.conf; import org.apache.iotdb.commons.exception.StartupException; diff --git a/procedure/src/main/java/org/apache/iotdb/procedure/conf/ProcedureNodeConstant.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/conf/ProcedureNodeConstant.java similarity index 96% rename from procedure/src/main/java/org/apache/iotdb/procedure/conf/ProcedureNodeConstant.java rename to confignode/src/main/java/org/apache/iotdb/confignode/procedure/conf/ProcedureNodeConstant.java index 750a97ab11ec..fd148a14f100 100644 --- a/procedure/src/main/java/org/apache/iotdb/procedure/conf/ProcedureNodeConstant.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/conf/ProcedureNodeConstant.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.procedure.conf; +package org.apache.iotdb.confignode.procedure.conf; public class ProcedureNodeConstant { public static final String PROCEDURE_WAL_SUFFIX = ".proc.wal"; diff --git a/procedure/src/main/java/org/apache/iotdb/procedure/exception/ProcedureAbortedException.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/exception/ProcedureAbortedException.java similarity index 94% rename from procedure/src/main/java/org/apache/iotdb/procedure/exception/ProcedureAbortedException.java rename to confignode/src/main/java/org/apache/iotdb/confignode/procedure/exception/ProcedureAbortedException.java index 51143ca14fcd..31d10e263478 100644 --- a/procedure/src/main/java/org/apache/iotdb/procedure/exception/ProcedureAbortedException.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/exception/ProcedureAbortedException.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.procedure.exception; +package org.apache.iotdb.confignode.procedure.exception; public class ProcedureAbortedException extends ProcedureException { public ProcedureAbortedException() { diff --git a/procedure/src/main/java/org/apache/iotdb/procedure/exception/ProcedureException.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/exception/ProcedureException.java similarity index 95% rename from procedure/src/main/java/org/apache/iotdb/procedure/exception/ProcedureException.java rename to confignode/src/main/java/org/apache/iotdb/confignode/procedure/exception/ProcedureException.java index 7a9749d76d50..923737f7ebd0 100644 --- a/procedure/src/main/java/org/apache/iotdb/procedure/exception/ProcedureException.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/exception/ProcedureException.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.procedure.exception; +package org.apache.iotdb.confignode.procedure.exception; public class ProcedureException extends Exception { /** default constructor */ diff --git a/procedure/src/main/java/org/apache/iotdb/procedure/exception/ProcedureSuspendedException.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/exception/ProcedureSuspendedException.java similarity index 95% rename from procedure/src/main/java/org/apache/iotdb/procedure/exception/ProcedureSuspendedException.java rename to confignode/src/main/java/org/apache/iotdb/confignode/procedure/exception/ProcedureSuspendedException.java index 7e9dbdab3cbe..1b6f049bd48b 100644 --- a/procedure/src/main/java/org/apache/iotdb/procedure/exception/ProcedureSuspendedException.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/exception/ProcedureSuspendedException.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.procedure.exception; +package org.apache.iotdb.confignode.procedure.exception; public class ProcedureSuspendedException extends ProcedureException { diff --git a/procedure/src/main/java/org/apache/iotdb/procedure/exception/ProcedureTimeoutException.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/exception/ProcedureTimeoutException.java similarity index 94% rename from procedure/src/main/java/org/apache/iotdb/procedure/exception/ProcedureTimeoutException.java rename to confignode/src/main/java/org/apache/iotdb/confignode/procedure/exception/ProcedureTimeoutException.java index 422dad201c46..ccc1e4b2d8ec 100644 --- a/procedure/src/main/java/org/apache/iotdb/procedure/exception/ProcedureTimeoutException.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/exception/ProcedureTimeoutException.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.procedure.exception; +package org.apache.iotdb.confignode.procedure.exception; public class ProcedureTimeoutException extends ProcedureException { public ProcedureTimeoutException(String s) { diff --git a/procedure/src/main/java/org/apache/iotdb/procedure/exception/ProcedureYieldException.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/exception/ProcedureYieldException.java similarity index 95% rename from procedure/src/main/java/org/apache/iotdb/procedure/exception/ProcedureYieldException.java rename to confignode/src/main/java/org/apache/iotdb/confignode/procedure/exception/ProcedureYieldException.java index 3f5c69a069b7..d3ed088c57eb 100644 --- a/procedure/src/main/java/org/apache/iotdb/procedure/exception/ProcedureYieldException.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/exception/ProcedureYieldException.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.procedure.exception; +package org.apache.iotdb.confignode.procedure.exception; public class ProcedureYieldException extends ProcedureException { /** default constructor */ diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/procedure/DeleteStorageGroupProcedure.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/impl/DeleteStorageGroupProcedure.java similarity index 90% rename from confignode/src/main/java/org/apache/iotdb/confignode/procedure/DeleteStorageGroupProcedure.java rename to confignode/src/main/java/org/apache/iotdb/confignode/procedure/impl/DeleteStorageGroupProcedure.java index c174d9bccfad..edea9369a68f 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/procedure/DeleteStorageGroupProcedure.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/impl/DeleteStorageGroupProcedure.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.confignode.procedure; +package org.apache.iotdb.confignode.procedure.impl; import org.apache.iotdb.common.rpc.thrift.TConsensusGroupId; import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation; @@ -29,15 +29,17 @@ import org.apache.iotdb.commons.utils.ThriftConfigNodeSerDeUtils; import org.apache.iotdb.confignode.consensus.request.write.DeleteStorageGroupReq; import org.apache.iotdb.confignode.manager.ConfigManager; +import org.apache.iotdb.confignode.procedure.Procedure; +import org.apache.iotdb.confignode.procedure.StateMachineProcedure; import org.apache.iotdb.confignode.procedure.env.ConfigNodeProcedureEnv; +import org.apache.iotdb.confignode.procedure.exception.ProcedureException; +import org.apache.iotdb.confignode.procedure.exception.ProcedureSuspendedException; +import org.apache.iotdb.confignode.procedure.exception.ProcedureYieldException; +import org.apache.iotdb.confignode.procedure.state.DeleteStorageGroupState; +import org.apache.iotdb.confignode.procedure.store.ProcedureFactory; import org.apache.iotdb.confignode.rpc.thrift.TStorageGroupSchema; import org.apache.iotdb.mpp.rpc.thrift.InternalService; import org.apache.iotdb.mpp.rpc.thrift.TInvalidateCacheReq; -import org.apache.iotdb.procedure.Procedure; -import org.apache.iotdb.procedure.StateMachineProcedure; -import org.apache.iotdb.procedure.exception.ProcedureException; -import org.apache.iotdb.procedure.exception.ProcedureSuspendedException; -import org.apache.iotdb.procedure.exception.ProcedureYieldException; import org.apache.thrift.TException; import org.slf4j.Logger; @@ -125,7 +127,9 @@ protected Flow executeFromState(ConfigNodeProcedureEnv env, DeleteStorageGroupSt } setNextState(DeleteStorageGroupState.INVALIDATE_CACHE); } else if (getCycles() > retryThreshold) { - setFailure(new ProcedureException("Delete config info id failed, status is " + status)); + setFailure( + new org.apache.iotdb.confignode.procedure.exception.ProcedureException( + "Delete config info id failed, status is " + status)); } break; case INVALIDATE_CACHE: @@ -140,7 +144,9 @@ protected Flow executeFromState(ConfigNodeProcedureEnv env, DeleteStorageGroupSt state, e); if (getCycles() > retryThreshold) { - setFailure(new ProcedureException("State stack at " + state)); + setFailure( + new org.apache.iotdb.confignode.procedure.exception.ProcedureException( + "State stack at " + state)); } } return Flow.HAS_MORE_STATE; @@ -165,7 +171,7 @@ private boolean deleteRegion( if (status.getCode() != StatusUtils.OK.getCode()) { if (getCycles() > retryThreshold) { setFailure( - new ProcedureException( + new org.apache.iotdb.confignode.procedure.exception.ProcedureException( "Delete data region id=" + regionId + " failed, status is " + status)); } return false; diff --git a/procedure/src/main/java/org/apache/iotdb/procedure/scheduler/AbstractProcedureScheduler.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/scheduler/AbstractProcedureScheduler.java similarity index 98% rename from procedure/src/main/java/org/apache/iotdb/procedure/scheduler/AbstractProcedureScheduler.java rename to confignode/src/main/java/org/apache/iotdb/confignode/procedure/scheduler/AbstractProcedureScheduler.java index be98b3986749..00b1a7d1b1f8 100644 --- a/procedure/src/main/java/org/apache/iotdb/procedure/scheduler/AbstractProcedureScheduler.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/scheduler/AbstractProcedureScheduler.java @@ -17,9 +17,9 @@ * under the License. */ -package org.apache.iotdb.procedure.scheduler; +package org.apache.iotdb.confignode.procedure.scheduler; -import org.apache.iotdb.procedure.Procedure; +import org.apache.iotdb.confignode.procedure.Procedure; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/procedure/src/main/java/org/apache/iotdb/procedure/scheduler/ProcedureScheduler.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/scheduler/ProcedureScheduler.java similarity index 96% rename from procedure/src/main/java/org/apache/iotdb/procedure/scheduler/ProcedureScheduler.java rename to confignode/src/main/java/org/apache/iotdb/confignode/procedure/scheduler/ProcedureScheduler.java index b582e80502eb..33ff7cf04d37 100644 --- a/procedure/src/main/java/org/apache/iotdb/procedure/scheduler/ProcedureScheduler.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/scheduler/ProcedureScheduler.java @@ -17,9 +17,9 @@ * under the License. */ -package org.apache.iotdb.procedure.scheduler; +package org.apache.iotdb.confignode.procedure.scheduler; -import org.apache.iotdb.procedure.Procedure; +import org.apache.iotdb.confignode.procedure.Procedure; import java.util.concurrent.TimeUnit; diff --git a/procedure/src/main/java/org/apache/iotdb/procedure/scheduler/SimpleProcedureScheduler.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/scheduler/SimpleProcedureScheduler.java similarity index 94% rename from procedure/src/main/java/org/apache/iotdb/procedure/scheduler/SimpleProcedureScheduler.java rename to confignode/src/main/java/org/apache/iotdb/confignode/procedure/scheduler/SimpleProcedureScheduler.java index 7024ddc25f8e..f6065a585ae1 100644 --- a/procedure/src/main/java/org/apache/iotdb/procedure/scheduler/SimpleProcedureScheduler.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/scheduler/SimpleProcedureScheduler.java @@ -17,9 +17,9 @@ * under the License. */ -package org.apache.iotdb.procedure.scheduler; +package org.apache.iotdb.confignode.procedure.scheduler; -import org.apache.iotdb.procedure.Procedure; +import org.apache.iotdb.confignode.procedure.Procedure; import java.util.ArrayDeque; diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/procedure/DeleteStorageGroupState.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/state/DeleteStorageGroupState.java similarity index 94% rename from confignode/src/main/java/org/apache/iotdb/confignode/procedure/DeleteStorageGroupState.java rename to confignode/src/main/java/org/apache/iotdb/confignode/procedure/state/DeleteStorageGroupState.java index 5e865e2fd11c..4ac0531dd294 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/procedure/DeleteStorageGroupState.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/state/DeleteStorageGroupState.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.confignode.procedure; +package org.apache.iotdb.confignode.procedure.state; public enum DeleteStorageGroupState { DELETE_STORAGE_GROUP_PREPARE, diff --git a/procedure/src/main/java/org/apache/iotdb/procedure/ProcedureLockState.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/state/ProcedureLockState.java similarity index 94% rename from procedure/src/main/java/org/apache/iotdb/procedure/ProcedureLockState.java rename to confignode/src/main/java/org/apache/iotdb/confignode/procedure/state/ProcedureLockState.java index 2c98174a3620..9098cfc163d5 100644 --- a/procedure/src/main/java/org/apache/iotdb/procedure/ProcedureLockState.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/state/ProcedureLockState.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.procedure; +package org.apache.iotdb.confignode.procedure.state; public enum ProcedureLockState { LOCK_ACQUIRED, diff --git a/procedure/src/main/java/org/apache/iotdb/procedure/service/ProcedureNodeMBean.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/state/ProcedureState.java similarity index 82% rename from procedure/src/main/java/org/apache/iotdb/procedure/service/ProcedureNodeMBean.java rename to confignode/src/main/java/org/apache/iotdb/confignode/procedure/state/ProcedureState.java index 05e734eaaa77..24db4ec60391 100644 --- a/procedure/src/main/java/org/apache/iotdb/procedure/service/ProcedureNodeMBean.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/state/ProcedureState.java @@ -17,6 +17,14 @@ * under the License. */ -package org.apache.iotdb.procedure.service; +package org.apache.iotdb.confignode.procedure.state; -public interface ProcedureNodeMBean {} +public enum ProcedureState { + INITIALIZING, + RUNNABLE, + WAITING, + WAITING_TIMEOUT, + ROLLEDBACK, + SUCCESS, + FAILED +} diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/procedure/ConfigProcedureStore.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/store/ConfigProcedureStore.java similarity index 91% rename from confignode/src/main/java/org/apache/iotdb/confignode/procedure/ConfigProcedureStore.java rename to confignode/src/main/java/org/apache/iotdb/confignode/procedure/store/ConfigProcedureStore.java index 8a9eb977b77e..a6c2512e6b2e 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/procedure/ConfigProcedureStore.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/store/ConfigProcedureStore.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.confignode.procedure; +package org.apache.iotdb.confignode.procedure.store; import org.apache.iotdb.commons.conf.CommonDescriptor; import org.apache.iotdb.confignode.consensus.request.write.DeleteProcedureReq; @@ -25,9 +25,7 @@ import org.apache.iotdb.confignode.manager.ConfigManager; import org.apache.iotdb.confignode.manager.ConsensusManager; import org.apache.iotdb.confignode.persistence.ProcedureInfo; -import org.apache.iotdb.procedure.Procedure; -import org.apache.iotdb.procedure.store.IProcedureStore; -import org.apache.iotdb.procedure.store.ProcedureStore; +import org.apache.iotdb.confignode.procedure.Procedure; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -71,12 +69,12 @@ public void setRunning(boolean running) { } @Override - public void load(List procedureList) { + public void load(List procedureList) { procedureInfo.load(procedureList); } @Override - public void update(Procedure procedure) { + public void update(org.apache.iotdb.confignode.procedure.Procedure procedure) { UpdateProcedureReq updateProcedureReq = new UpdateProcedureReq(); ProcedureFactory.ProcedureType procedureType = ProcedureFactory.getProcedureType(procedure); if (procedureType != null) { @@ -86,7 +84,7 @@ public void update(Procedure procedure) { } @Override - public void update(Procedure[] subprocs) { + public void update(org.apache.iotdb.confignode.procedure.Procedure[] subprocs) { for (Procedure subproc : subprocs) { update(subproc); } diff --git a/procedure/src/main/java/org/apache/iotdb/procedure/store/IProcedureFactory.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/store/IProcedureFactory.java similarity index 89% rename from procedure/src/main/java/org/apache/iotdb/procedure/store/IProcedureFactory.java rename to confignode/src/main/java/org/apache/iotdb/confignode/procedure/store/IProcedureFactory.java index 85330d679dd5..d080c928d4fe 100644 --- a/procedure/src/main/java/org/apache/iotdb/procedure/store/IProcedureFactory.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/store/IProcedureFactory.java @@ -17,9 +17,9 @@ * under the License. */ -package org.apache.iotdb.procedure.store; +package org.apache.iotdb.confignode.procedure.store; -import org.apache.iotdb.procedure.Procedure; +import org.apache.iotdb.confignode.procedure.Procedure; import java.io.IOException; import java.nio.ByteBuffer; diff --git a/procedure/src/main/java/org/apache/iotdb/procedure/store/IProcedureStore.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/store/IProcedureStore.java similarity index 81% rename from procedure/src/main/java/org/apache/iotdb/procedure/store/IProcedureStore.java rename to confignode/src/main/java/org/apache/iotdb/confignode/procedure/store/IProcedureStore.java index 439160afb83d..3e9542f0fc8c 100644 --- a/procedure/src/main/java/org/apache/iotdb/procedure/store/IProcedureStore.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/store/IProcedureStore.java @@ -17,9 +17,9 @@ * under the License. */ -package org.apache.iotdb.procedure.store; +package org.apache.iotdb.confignode.procedure.store; -import org.apache.iotdb.procedure.Procedure; +import org.apache.iotdb.confignode.procedure.Procedure; import java.util.List; @@ -29,9 +29,9 @@ public interface IProcedureStore { void setRunning(boolean running); - void load(List procedureList); + void load(List procedureList); - void update(Procedure procedure); + void update(org.apache.iotdb.confignode.procedure.Procedure procedure); void update(Procedure[] subprocs); diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/procedure/ProcedureFactory.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/store/ProcedureFactory.java similarity index 84% rename from confignode/src/main/java/org/apache/iotdb/confignode/procedure/ProcedureFactory.java rename to confignode/src/main/java/org/apache/iotdb/confignode/procedure/store/ProcedureFactory.java index 9d566236be26..907a34c369dc 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/procedure/ProcedureFactory.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/store/ProcedureFactory.java @@ -17,10 +17,10 @@ * under the License. */ -package org.apache.iotdb.confignode.procedure; +package org.apache.iotdb.confignode.procedure.store; -import org.apache.iotdb.procedure.Procedure; -import org.apache.iotdb.procedure.store.IProcedureFactory; +import org.apache.iotdb.confignode.procedure.Procedure; +import org.apache.iotdb.confignode.procedure.impl.DeleteStorageGroupProcedure; import java.io.IOException; import java.nio.ByteBuffer; @@ -28,13 +28,14 @@ public class ProcedureFactory implements IProcedureFactory { @Override - public Procedure create(ByteBuffer buffer) throws IOException { + public org.apache.iotdb.confignode.procedure.Procedure create(ByteBuffer buffer) + throws IOException { int typeNum = buffer.getInt(); if (typeNum >= ProcedureType.values().length) { throw new IOException("unrecognized log type " + typeNum); } ProcedureType type = ProcedureType.values()[typeNum]; - Procedure procedure; + org.apache.iotdb.confignode.procedure.Procedure procedure; switch (type) { case DELETE_STORAGE_GROUP_PROCEDURE: procedure = new DeleteStorageGroupProcedure(); diff --git a/procedure/src/main/java/org/apache/iotdb/procedure/store/ProcedureStore.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/store/ProcedureStore.java similarity index 95% rename from procedure/src/main/java/org/apache/iotdb/procedure/store/ProcedureStore.java rename to confignode/src/main/java/org/apache/iotdb/confignode/procedure/store/ProcedureStore.java index 1ca2c33cc072..29e4a39d64b6 100644 --- a/procedure/src/main/java/org/apache/iotdb/procedure/store/ProcedureStore.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/store/ProcedureStore.java @@ -17,12 +17,12 @@ * under the License. */ -package org.apache.iotdb.procedure.store; +package org.apache.iotdb.confignode.procedure.store; import org.apache.iotdb.commons.utils.TestOnly; -import org.apache.iotdb.procedure.Procedure; -import org.apache.iotdb.procedure.conf.ProcedureNodeConfigDescriptor; -import org.apache.iotdb.procedure.conf.ProcedureNodeConstant; +import org.apache.iotdb.confignode.procedure.Procedure; +import org.apache.iotdb.confignode.procedure.conf.ProcedureNodeConfigDescriptor; +import org.apache.iotdb.confignode.procedure.conf.ProcedureNodeConstant; import org.apache.commons.io.FileUtils; import org.slf4j.Logger; diff --git a/procedure/src/main/java/org/apache/iotdb/procedure/store/ProcedureWAL.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/store/ProcedureWAL.java similarity index 96% rename from procedure/src/main/java/org/apache/iotdb/procedure/store/ProcedureWAL.java rename to confignode/src/main/java/org/apache/iotdb/confignode/procedure/store/ProcedureWAL.java index 2960fb18c131..32dce959dc89 100644 --- a/procedure/src/main/java/org/apache/iotdb/procedure/store/ProcedureWAL.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/store/ProcedureWAL.java @@ -17,9 +17,9 @@ * under the License. */ -package org.apache.iotdb.procedure.store; +package org.apache.iotdb.confignode.procedure.store; -import org.apache.iotdb.procedure.Procedure; +import org.apache.iotdb.confignode.procedure.Procedure; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/confignode/src/test/java/org/apache/iotdb/confignode/consensus/request/ConfigRequestSerDeTest.java b/confignode/src/test/java/org/apache/iotdb/confignode/consensus/request/ConfigRequestSerDeTest.java index 89f9cdfefade..eaad26db7bd3 100644 --- a/confignode/src/test/java/org/apache/iotdb/confignode/consensus/request/ConfigRequestSerDeTest.java +++ b/confignode/src/test/java/org/apache/iotdb/confignode/consensus/request/ConfigRequestSerDeTest.java @@ -50,9 +50,9 @@ import org.apache.iotdb.confignode.consensus.request.write.SetTTLReq; import org.apache.iotdb.confignode.consensus.request.write.SetTimePartitionIntervalReq; import org.apache.iotdb.confignode.consensus.request.write.UpdateProcedureReq; -import org.apache.iotdb.confignode.procedure.DeleteStorageGroupProcedure; +import org.apache.iotdb.confignode.procedure.Procedure; +import org.apache.iotdb.confignode.procedure.impl.DeleteStorageGroupProcedure; import org.apache.iotdb.confignode.rpc.thrift.TStorageGroupSchema; -import org.apache.iotdb.procedure.Procedure; import org.junit.After; import org.junit.Assert; diff --git a/procedure/src/test/java/org/apache/iotdb/procedure/NoopProcedureStore.java b/confignode/src/test/java/org/apache/iotdb/confignode/procedure/NoopProcedureStore.java similarity index 84% rename from procedure/src/test/java/org/apache/iotdb/procedure/NoopProcedureStore.java rename to confignode/src/test/java/org/apache/iotdb/confignode/procedure/NoopProcedureStore.java index 35875af12efe..37464a5020d0 100644 --- a/procedure/src/test/java/org/apache/iotdb/procedure/NoopProcedureStore.java +++ b/confignode/src/test/java/org/apache/iotdb/confignode/procedure/NoopProcedureStore.java @@ -17,9 +17,9 @@ * under the License. */ -package org.apache.iotdb.procedure; +package org.apache.iotdb.confignode.procedure; -import org.apache.iotdb.procedure.store.IProcedureStore; +import org.apache.iotdb.confignode.procedure.store.IProcedureStore; import java.util.List; @@ -38,10 +38,10 @@ public void setRunning(boolean running) { } @Override - public void load(List procedureList) {} + public void load(List procedureList) {} @Override - public void update(Procedure procedure) {} + public void update(org.apache.iotdb.confignode.procedure.Procedure procedure) {} @Override public void update(Procedure[] subprocs) {} diff --git a/procedure/src/test/java/org/apache/iotdb/procedure/TestLockRegime.java b/confignode/src/test/java/org/apache/iotdb/confignode/procedure/TestLockRegime.java similarity index 89% rename from procedure/src/test/java/org/apache/iotdb/procedure/TestLockRegime.java rename to confignode/src/test/java/org/apache/iotdb/confignode/procedure/TestLockRegime.java index 82e19013d0e8..500a51e9e3d0 100644 --- a/procedure/src/test/java/org/apache/iotdb/procedure/TestLockRegime.java +++ b/confignode/src/test/java/org/apache/iotdb/confignode/procedure/TestLockRegime.java @@ -17,10 +17,10 @@ * under the License. */ -package org.apache.iotdb.procedure; +package org.apache.iotdb.confignode.procedure; -import org.apache.iotdb.procedure.entity.SimpleLockProcedure; -import org.apache.iotdb.procedure.util.ProcedureTestUtil; +import org.apache.iotdb.confignode.procedure.entity.SimpleLockProcedure; +import org.apache.iotdb.confignode.procedure.util.ProcedureTestUtil; import org.junit.Assert; import org.junit.Test; diff --git a/procedure/src/test/java/org/apache/iotdb/procedure/TestProcEnv.java b/confignode/src/test/java/org/apache/iotdb/confignode/procedure/TestProcEnv.java similarity index 93% rename from procedure/src/test/java/org/apache/iotdb/procedure/TestProcEnv.java rename to confignode/src/test/java/org/apache/iotdb/confignode/procedure/TestProcEnv.java index 3bf0bd6c0f80..cfe77585a5ad 100644 --- a/procedure/src/test/java/org/apache/iotdb/procedure/TestProcEnv.java +++ b/confignode/src/test/java/org/apache/iotdb/confignode/procedure/TestProcEnv.java @@ -17,9 +17,9 @@ * under the License. */ -package org.apache.iotdb.procedure; +package org.apache.iotdb.confignode.procedure; -import org.apache.iotdb.procedure.scheduler.ProcedureScheduler; +import org.apache.iotdb.confignode.procedure.scheduler.ProcedureScheduler; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; diff --git a/procedure/src/test/java/org/apache/iotdb/procedure/TestProcedureBase.java b/confignode/src/test/java/org/apache/iotdb/confignode/procedure/TestProcedureBase.java similarity index 82% rename from procedure/src/test/java/org/apache/iotdb/procedure/TestProcedureBase.java rename to confignode/src/test/java/org/apache/iotdb/confignode/procedure/TestProcedureBase.java index 9edf1af549e9..3176426feb39 100644 --- a/procedure/src/test/java/org/apache/iotdb/procedure/TestProcedureBase.java +++ b/confignode/src/test/java/org/apache/iotdb/confignode/procedure/TestProcedureBase.java @@ -17,9 +17,9 @@ * under the License. */ -package org.apache.iotdb.procedure; +package org.apache.iotdb.confignode.procedure; -import org.apache.iotdb.procedure.store.IProcedureStore; +import org.apache.iotdb.confignode.procedure.store.IProcedureStore; import org.junit.After; import org.junit.Before; @@ -32,7 +32,7 @@ public class TestProcedureBase { protected TestProcEnv env; protected IProcedureStore procStore; - protected ProcedureExecutor procExecutor; + protected org.apache.iotdb.confignode.procedure.ProcedureExecutor procExecutor; @Before public void setUp() { @@ -52,7 +52,8 @@ public void tearDown() { protected void initExecutor() { this.env = new TestProcEnv(); this.procStore = new NoopProcedureStore(); - this.procExecutor = new ProcedureExecutor<>(env, procStore); + this.procExecutor = + new org.apache.iotdb.confignode.procedure.ProcedureExecutor<>(env, procStore); this.env.setScheduler(this.procExecutor.getScheduler()); this.procExecutor.init(4); } @@ -73,7 +74,7 @@ public void setProcStore(IProcedureStore procStore) { this.procStore = procStore; } - public ProcedureExecutor getProcExecutor() { + public org.apache.iotdb.confignode.procedure.ProcedureExecutor getProcExecutor() { return procExecutor; } diff --git a/procedure/src/test/java/org/apache/iotdb/procedure/TestProcedureExecutor.java b/confignode/src/test/java/org/apache/iotdb/confignode/procedure/TestProcedureExecutor.java similarity index 92% rename from procedure/src/test/java/org/apache/iotdb/procedure/TestProcedureExecutor.java rename to confignode/src/test/java/org/apache/iotdb/confignode/procedure/TestProcedureExecutor.java index 1f750115112c..f007e7dce39d 100644 --- a/procedure/src/test/java/org/apache/iotdb/procedure/TestProcedureExecutor.java +++ b/confignode/src/test/java/org/apache/iotdb/confignode/procedure/TestProcedureExecutor.java @@ -17,12 +17,12 @@ * under the License. */ -package org.apache.iotdb.procedure; +package org.apache.iotdb.confignode.procedure; -import org.apache.iotdb.procedure.entity.IncProcedure; -import org.apache.iotdb.procedure.entity.NoopProcedure; -import org.apache.iotdb.procedure.entity.StuckProcedure; -import org.apache.iotdb.procedure.util.ProcedureTestUtil; +import org.apache.iotdb.confignode.procedure.entity.IncProcedure; +import org.apache.iotdb.confignode.procedure.entity.NoopProcedure; +import org.apache.iotdb.confignode.procedure.entity.StuckProcedure; +import org.apache.iotdb.confignode.procedure.util.ProcedureTestUtil; import org.junit.Assert; import org.junit.Test; diff --git a/procedure/src/test/java/org/apache/iotdb/procedure/TestSTMProcedure.java b/confignode/src/test/java/org/apache/iotdb/confignode/procedure/TestSTMProcedure.java similarity index 91% rename from procedure/src/test/java/org/apache/iotdb/procedure/TestSTMProcedure.java rename to confignode/src/test/java/org/apache/iotdb/confignode/procedure/TestSTMProcedure.java index 286dcbb165c3..6c4ab3234d9c 100644 --- a/procedure/src/test/java/org/apache/iotdb/procedure/TestSTMProcedure.java +++ b/confignode/src/test/java/org/apache/iotdb/confignode/procedure/TestSTMProcedure.java @@ -17,10 +17,10 @@ * under the License. */ -package org.apache.iotdb.procedure; +package org.apache.iotdb.confignode.procedure; -import org.apache.iotdb.procedure.entity.SimpleSTMProcedure; -import org.apache.iotdb.procedure.util.ProcedureTestUtil; +import org.apache.iotdb.confignode.procedure.entity.SimpleSTMProcedure; +import org.apache.iotdb.confignode.procedure.util.ProcedureTestUtil; import org.junit.Assert; import org.junit.Test; diff --git a/procedure/src/test/java/org/apache/iotdb/procedure/entity/IncProcedure.java b/confignode/src/test/java/org/apache/iotdb/confignode/procedure/entity/IncProcedure.java similarity index 85% rename from procedure/src/test/java/org/apache/iotdb/procedure/entity/IncProcedure.java rename to confignode/src/test/java/org/apache/iotdb/confignode/procedure/entity/IncProcedure.java index 5459d082312a..b5c8c9b5434c 100644 --- a/procedure/src/test/java/org/apache/iotdb/procedure/entity/IncProcedure.java +++ b/confignode/src/test/java/org/apache/iotdb/confignode/procedure/entity/IncProcedure.java @@ -17,12 +17,12 @@ * under the License. */ -package org.apache.iotdb.procedure.entity; +package org.apache.iotdb.confignode.procedure.entity; -import org.apache.iotdb.procedure.Procedure; -import org.apache.iotdb.procedure.TestProcEnv; -import org.apache.iotdb.procedure.exception.ProcedureSuspendedException; -import org.apache.iotdb.procedure.exception.ProcedureYieldException; +import org.apache.iotdb.confignode.procedure.Procedure; +import org.apache.iotdb.confignode.procedure.TestProcEnv; +import org.apache.iotdb.confignode.procedure.exception.ProcedureSuspendedException; +import org.apache.iotdb.confignode.procedure.exception.ProcedureYieldException; import java.io.IOException; import java.nio.ByteBuffer; diff --git a/procedure/src/test/java/org/apache/iotdb/procedure/entity/NoopProcedure.java b/confignode/src/test/java/org/apache/iotdb/confignode/procedure/entity/NoopProcedure.java similarity index 79% rename from procedure/src/test/java/org/apache/iotdb/procedure/entity/NoopProcedure.java rename to confignode/src/test/java/org/apache/iotdb/confignode/procedure/entity/NoopProcedure.java index 7afe81860b9c..f2250ec32e7a 100644 --- a/procedure/src/test/java/org/apache/iotdb/procedure/entity/NoopProcedure.java +++ b/confignode/src/test/java/org/apache/iotdb/confignode/procedure/entity/NoopProcedure.java @@ -17,12 +17,12 @@ * under the License. */ -package org.apache.iotdb.procedure.entity; +package org.apache.iotdb.confignode.procedure.entity; -import org.apache.iotdb.procedure.Procedure; -import org.apache.iotdb.procedure.TestProcEnv; -import org.apache.iotdb.procedure.exception.ProcedureSuspendedException; -import org.apache.iotdb.procedure.exception.ProcedureYieldException; +import org.apache.iotdb.confignode.procedure.Procedure; +import org.apache.iotdb.confignode.procedure.TestProcEnv; +import org.apache.iotdb.confignode.procedure.exception.ProcedureSuspendedException; +import org.apache.iotdb.confignode.procedure.exception.ProcedureYieldException; import java.io.IOException; diff --git a/procedure/src/test/java/org/apache/iotdb/procedure/entity/SimpleLockProcedure.java b/confignode/src/test/java/org/apache/iotdb/confignode/procedure/entity/SimpleLockProcedure.java similarity index 83% rename from procedure/src/test/java/org/apache/iotdb/procedure/entity/SimpleLockProcedure.java rename to confignode/src/test/java/org/apache/iotdb/confignode/procedure/entity/SimpleLockProcedure.java index c0710962c150..98cd53edc668 100644 --- a/procedure/src/test/java/org/apache/iotdb/procedure/entity/SimpleLockProcedure.java +++ b/confignode/src/test/java/org/apache/iotdb/confignode/procedure/entity/SimpleLockProcedure.java @@ -17,14 +17,14 @@ * under the License. */ -package org.apache.iotdb.procedure.entity; +package org.apache.iotdb.confignode.procedure.entity; -import org.apache.iotdb.procedure.Procedure; -import org.apache.iotdb.procedure.ProcedureLockState; -import org.apache.iotdb.procedure.TestProcEnv; -import org.apache.iotdb.procedure.exception.ProcedureSuspendedException; -import org.apache.iotdb.procedure.exception.ProcedureYieldException; -import org.apache.iotdb.procedure.scheduler.SimpleProcedureScheduler; +import org.apache.iotdb.confignode.procedure.Procedure; +import org.apache.iotdb.confignode.procedure.TestProcEnv; +import org.apache.iotdb.confignode.procedure.exception.ProcedureSuspendedException; +import org.apache.iotdb.confignode.procedure.exception.ProcedureYieldException; +import org.apache.iotdb.confignode.procedure.scheduler.SimpleProcedureScheduler; +import org.apache.iotdb.confignode.procedure.state.ProcedureLockState; import java.io.IOException; diff --git a/procedure/src/test/java/org/apache/iotdb/procedure/entity/SimpleSTMProcedure.java b/confignode/src/test/java/org/apache/iotdb/confignode/procedure/entity/SimpleSTMProcedure.java similarity index 86% rename from procedure/src/test/java/org/apache/iotdb/procedure/entity/SimpleSTMProcedure.java rename to confignode/src/test/java/org/apache/iotdb/confignode/procedure/entity/SimpleSTMProcedure.java index 1ab2c36c0d31..a95905ca766a 100644 --- a/procedure/src/test/java/org/apache/iotdb/procedure/entity/SimpleSTMProcedure.java +++ b/confignode/src/test/java/org/apache/iotdb/confignode/procedure/entity/SimpleSTMProcedure.java @@ -17,13 +17,13 @@ * under the License. */ -package org.apache.iotdb.procedure.entity; +package org.apache.iotdb.confignode.procedure.entity; -import org.apache.iotdb.procedure.StateMachineProcedure; -import org.apache.iotdb.procedure.TestProcEnv; -import org.apache.iotdb.procedure.exception.ProcedureException; -import org.apache.iotdb.procedure.exception.ProcedureSuspendedException; -import org.apache.iotdb.procedure.exception.ProcedureYieldException; +import org.apache.iotdb.confignode.procedure.StateMachineProcedure; +import org.apache.iotdb.confignode.procedure.TestProcEnv; +import org.apache.iotdb.confignode.procedure.exception.ProcedureException; +import org.apache.iotdb.confignode.procedure.exception.ProcedureSuspendedException; +import org.apache.iotdb.confignode.procedure.exception.ProcedureYieldException; import java.io.IOException; import java.util.concurrent.atomic.AtomicInteger; diff --git a/procedure/src/test/java/org/apache/iotdb/procedure/entity/SleepProcedure.java b/confignode/src/test/java/org/apache/iotdb/confignode/procedure/entity/SleepProcedure.java similarity index 80% rename from procedure/src/test/java/org/apache/iotdb/procedure/entity/SleepProcedure.java rename to confignode/src/test/java/org/apache/iotdb/confignode/procedure/entity/SleepProcedure.java index fc14786838dd..3e2816fed4c4 100644 --- a/procedure/src/test/java/org/apache/iotdb/procedure/entity/SleepProcedure.java +++ b/confignode/src/test/java/org/apache/iotdb/confignode/procedure/entity/SleepProcedure.java @@ -17,13 +17,13 @@ * under the License. */ -package org.apache.iotdb.procedure.entity; +package org.apache.iotdb.confignode.procedure.entity; -import org.apache.iotdb.procedure.Procedure; -import org.apache.iotdb.procedure.TestProcEnv; -import org.apache.iotdb.procedure.exception.ProcedureSuspendedException; -import org.apache.iotdb.procedure.exception.ProcedureYieldException; -import org.apache.iotdb.procedure.util.ProcedureTestUtil; +import org.apache.iotdb.confignode.procedure.Procedure; +import org.apache.iotdb.confignode.procedure.TestProcEnv; +import org.apache.iotdb.confignode.procedure.exception.ProcedureSuspendedException; +import org.apache.iotdb.confignode.procedure.exception.ProcedureYieldException; +import org.apache.iotdb.confignode.procedure.util.ProcedureTestUtil; import java.io.IOException; import java.nio.ByteBuffer; diff --git a/procedure/src/test/java/org/apache/iotdb/procedure/entity/StuckProcedure.java b/confignode/src/test/java/org/apache/iotdb/confignode/procedure/entity/StuckProcedure.java similarity index 91% rename from procedure/src/test/java/org/apache/iotdb/procedure/entity/StuckProcedure.java rename to confignode/src/test/java/org/apache/iotdb/confignode/procedure/entity/StuckProcedure.java index 35f86bd15b5b..9363a7e79396 100644 --- a/procedure/src/test/java/org/apache/iotdb/procedure/entity/StuckProcedure.java +++ b/confignode/src/test/java/org/apache/iotdb/confignode/procedure/entity/StuckProcedure.java @@ -17,10 +17,10 @@ * under the License. */ -package org.apache.iotdb.procedure.entity; +package org.apache.iotdb.confignode.procedure.entity; -import org.apache.iotdb.procedure.Procedure; -import org.apache.iotdb.procedure.TestProcEnv; +import org.apache.iotdb.confignode.procedure.Procedure; +import org.apache.iotdb.confignode.procedure.TestProcEnv; import java.io.IOException; import java.util.concurrent.Semaphore; diff --git a/procedure/src/test/java/org/apache/iotdb/procedure/entity/StuckSTMProcedure.java b/confignode/src/test/java/org/apache/iotdb/confignode/procedure/entity/StuckSTMProcedure.java similarity index 87% rename from procedure/src/test/java/org/apache/iotdb/procedure/entity/StuckSTMProcedure.java rename to confignode/src/test/java/org/apache/iotdb/confignode/procedure/entity/StuckSTMProcedure.java index 1bab63028ab7..f78ae176d8f3 100644 --- a/procedure/src/test/java/org/apache/iotdb/procedure/entity/StuckSTMProcedure.java +++ b/confignode/src/test/java/org/apache/iotdb/confignode/procedure/entity/StuckSTMProcedure.java @@ -17,13 +17,13 @@ * under the License. */ -package org.apache.iotdb.procedure.entity; +package org.apache.iotdb.confignode.procedure.entity; -import org.apache.iotdb.procedure.StateMachineProcedure; -import org.apache.iotdb.procedure.TestProcEnv; -import org.apache.iotdb.procedure.exception.ProcedureException; -import org.apache.iotdb.procedure.exception.ProcedureSuspendedException; -import org.apache.iotdb.procedure.exception.ProcedureYieldException; +import org.apache.iotdb.confignode.procedure.StateMachineProcedure; +import org.apache.iotdb.confignode.procedure.TestProcEnv; +import org.apache.iotdb.confignode.procedure.exception.ProcedureException; +import org.apache.iotdb.confignode.procedure.exception.ProcedureSuspendedException; +import org.apache.iotdb.confignode.procedure.exception.ProcedureYieldException; import java.io.IOException; import java.nio.ByteBuffer; diff --git a/procedure/src/test/java/org/apache/iotdb/procedure/entity/TestProcedureFactory.java b/confignode/src/test/java/org/apache/iotdb/confignode/procedure/entity/TestProcedureFactory.java similarity index 92% rename from procedure/src/test/java/org/apache/iotdb/procedure/entity/TestProcedureFactory.java rename to confignode/src/test/java/org/apache/iotdb/confignode/procedure/entity/TestProcedureFactory.java index 1b248793f18b..421fe7cc46a4 100644 --- a/procedure/src/test/java/org/apache/iotdb/procedure/entity/TestProcedureFactory.java +++ b/confignode/src/test/java/org/apache/iotdb/confignode/procedure/entity/TestProcedureFactory.java @@ -17,10 +17,10 @@ * under the License. */ -package org.apache.iotdb.procedure.entity; +package org.apache.iotdb.confignode.procedure.entity; -import org.apache.iotdb.procedure.Procedure; -import org.apache.iotdb.procedure.store.IProcedureFactory; +import org.apache.iotdb.confignode.procedure.Procedure; +import org.apache.iotdb.confignode.procedure.store.IProcedureFactory; import java.io.IOException; import java.nio.ByteBuffer; diff --git a/procedure/src/test/java/org/apache/iotdb/procedure/store/TestProcedureStore.java b/confignode/src/test/java/org/apache/iotdb/confignode/procedure/store/TestProcedureStore.java similarity index 76% rename from procedure/src/test/java/org/apache/iotdb/procedure/store/TestProcedureStore.java rename to confignode/src/test/java/org/apache/iotdb/confignode/procedure/store/TestProcedureStore.java index 24091df56552..e7a51885f156 100644 --- a/procedure/src/test/java/org/apache/iotdb/procedure/store/TestProcedureStore.java +++ b/confignode/src/test/java/org/apache/iotdb/confignode/procedure/store/TestProcedureStore.java @@ -17,17 +17,17 @@ * under the License. */ -package org.apache.iotdb.procedure.store; +package org.apache.iotdb.confignode.procedure.store; -import org.apache.iotdb.procedure.Procedure; -import org.apache.iotdb.procedure.ProcedureExecutor; -import org.apache.iotdb.procedure.TestProcEnv; -import org.apache.iotdb.procedure.TestProcedureBase; -import org.apache.iotdb.procedure.entity.IncProcedure; -import org.apache.iotdb.procedure.entity.StuckSTMProcedure; -import org.apache.iotdb.procedure.entity.TestProcedureFactory; -import org.apache.iotdb.procedure.util.ProcedureTestUtil; -import org.apache.iotdb.service.rpc.thrift.ProcedureState; +import org.apache.iotdb.confignode.procedure.Procedure; +import org.apache.iotdb.confignode.procedure.ProcedureExecutor; +import org.apache.iotdb.confignode.procedure.TestProcEnv; +import org.apache.iotdb.confignode.procedure.TestProcedureBase; +import org.apache.iotdb.confignode.procedure.entity.IncProcedure; +import org.apache.iotdb.confignode.procedure.entity.StuckSTMProcedure; +import org.apache.iotdb.confignode.procedure.entity.TestProcedureFactory; +import org.apache.iotdb.confignode.procedure.state.ProcedureState; +import org.apache.iotdb.confignode.procedure.util.ProcedureTestUtil; import org.apache.commons.io.FileUtils; import org.junit.Assert; @@ -48,7 +48,8 @@ public class TestProcedureStore extends TestProcedureBase { @Override protected void initExecutor() { this.env = new TestProcEnv(); - this.procStore = new ProcedureStore(TEST_DIR, factory); + this.procStore = + new org.apache.iotdb.confignode.procedure.store.ProcedureStore(TEST_DIR, factory); this.procExecutor = new ProcedureExecutor<>(env, procStore); this.env.setScheduler(this.procExecutor.getScheduler()); this.procExecutor.init(WORK_THREAD); @@ -56,7 +57,8 @@ protected void initExecutor() { @Test public void testUpdate() { - ProcedureStore procedureStore = new ProcedureStore(TEST_DIR, factory); + org.apache.iotdb.confignode.procedure.store.ProcedureStore procedureStore = + new org.apache.iotdb.confignode.procedure.store.ProcedureStore(TEST_DIR, factory); IncProcedure incProcedure = new IncProcedure(); procedureStore.update(incProcedure); List procedureList = new ArrayList<>(); @@ -83,7 +85,8 @@ public void testChildProcedureLoad() { // stop service ProcedureTestUtil.stopService(procExecutor, procExecutor.getScheduler(), procStore); ConcurrentHashMap procedures = procExecutor.getProcedures(); - ProcedureStore procedureStore = new ProcedureStore(TEST_DIR, new TestProcedureFactory()); + org.apache.iotdb.confignode.procedure.store.ProcedureStore procedureStore = + new ProcedureStore(TEST_DIR, new TestProcedureFactory()); List procedureList = new ArrayList<>(); procedureStore.load(procedureList); Assert.assertEquals(childCount + 1, procedureList.size()); diff --git a/procedure/src/test/java/org/apache/iotdb/procedure/util/ProcedureTestUtil.java b/confignode/src/test/java/org/apache/iotdb/confignode/procedure/util/ProcedureTestUtil.java similarity index 88% rename from procedure/src/test/java/org/apache/iotdb/procedure/util/ProcedureTestUtil.java rename to confignode/src/test/java/org/apache/iotdb/confignode/procedure/util/ProcedureTestUtil.java index e23622381857..fbabd754ff34 100644 --- a/procedure/src/test/java/org/apache/iotdb/procedure/util/ProcedureTestUtil.java +++ b/confignode/src/test/java/org/apache/iotdb/confignode/procedure/util/ProcedureTestUtil.java @@ -17,11 +17,11 @@ * under the License. */ -package org.apache.iotdb.procedure.util; +package org.apache.iotdb.confignode.procedure.util; -import org.apache.iotdb.procedure.ProcedureExecutor; -import org.apache.iotdb.procedure.scheduler.ProcedureScheduler; -import org.apache.iotdb.procedure.store.IProcedureStore; +import org.apache.iotdb.confignode.procedure.ProcedureExecutor; +import org.apache.iotdb.confignode.procedure.scheduler.ProcedureScheduler; +import org.apache.iotdb.confignode.procedure.store.IProcedureStore; import java.util.concurrent.TimeUnit; diff --git a/confignode/src/test/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessorTest.java b/confignode/src/test/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessorTest.java index d4fb24adeead..2453255d54e6 100644 --- a/confignode/src/test/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessorTest.java +++ b/confignode/src/test/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessorTest.java @@ -37,7 +37,7 @@ import org.apache.iotdb.confignode.conf.ConfigNodeDescriptor; import org.apache.iotdb.confignode.conf.ConfigNodeStartupCheck; import org.apache.iotdb.confignode.manager.ConfigManager; -import org.apache.iotdb.confignode.procedure.DeleteStorageGroupProcedure; +import org.apache.iotdb.confignode.procedure.impl.DeleteStorageGroupProcedure; import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerReq; import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerResp; import org.apache.iotdb.confignode.rpc.thrift.TCheckUserPrivilegesReq; diff --git a/node-commons/src/main/java/org/apache/iotdb/commons/concurrent/ThreadName.java b/node-commons/src/main/java/org/apache/iotdb/commons/concurrent/ThreadName.java index a9222f36adf4..b6c3317a5bd6 100644 --- a/node-commons/src/main/java/org/apache/iotdb/commons/concurrent/ThreadName.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/concurrent/ThreadName.java @@ -80,9 +80,7 @@ public enum ThreadName { DATA_BLOCK_MANAGER_RPC_SERVER("DataBlockManagerRPC"), DATA_BLOCK_MANAGER_RPC_CLIENT("DataBlockManagerRPC-Client"), INTERNAL_SERVICE_RPC_SERVER("InternalServiceRPC"), - INTERNAL_SERVICE_RPC_CLIENT("InternalServiceRPC-Client"), - PROCEDURE_NODE_SERVER("ProcedureNode-Server"), - PROCEDURE_NODE_CLIENT("ProcedureNode-Client"); + INTERNAL_SERVICE_RPC_CLIENT("InternalServiceRPC-Client"); private final String name; diff --git a/node-commons/src/main/java/org/apache/iotdb/commons/service/ServiceType.java b/node-commons/src/main/java/org/apache/iotdb/commons/service/ServiceType.java index 6b068618ce08..1e913818ccf5 100644 --- a/node-commons/src/main/java/org/apache/iotdb/commons/service/ServiceType.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/service/ServiceType.java @@ -71,8 +71,7 @@ public enum ServiceType { DATA_NODE_MANAGEMENT_SERVICE("Data Node management service", "DataNodeManagementServer"), FRAGMENT_INSTANCE_MANAGER_SERVICE("Fragment instance manager", "FragmentInstanceManager"), DATA_BLOCK_MANAGER_SERVICE("Data block manager", "DataBlockManager"), - INTERNAL_SERVICE("Internal Service", "InternalService"), - PROCEDURE_SERVICE("Procedure Service", "ProcedureService"); + INTERNAL_SERVICE("Internal Service", "InternalService"); private final String name; private final String jmxName; diff --git a/pom.xml b/pom.xml index e9f082943637..78e63604095c 100644 --- a/pom.xml +++ b/pom.xml @@ -87,7 +87,6 @@ thrift-cluster thrift-sync thrift-influxdb - thrift-procedure service-rpc jdbc influxdb-protocol @@ -116,7 +115,6 @@ metrics integration consensus - procedure library-udf schema-engine-rocksdb diff --git a/procedure/pom.xml b/procedure/pom.xml deleted file mode 100644 index c067c2250c32..000000000000 --- a/procedure/pom.xml +++ /dev/null @@ -1,93 +0,0 @@ - - - - 4.0.0 - - org.apache.iotdb - iotdb-parent - 0.14.0-SNAPSHOT - ../pom.xml - - iotdb-procedure - procedure - - - - org.apache.thrift - libthrift - ${thrift.version} - - - commons-io - commons-io - - - org.apache.iotdb - iotdb-thrift-procedure - ${project.version} - - - org.apache.thrift - libthrift - - - compile - - - org.apache.iotdb - node-commons - ${project.version} - compile - - - commons-cli - commons-cli - - - org.awaitility - awaitility - ${awaitility.version} - test - - - - org.powermock - powermock-core - test - - - org.powermock - powermock-module-junit4 - test - - - org.powermock - powermock-api-mockito2 - test - - - org.apache.commons - commons-pool2 - - - diff --git a/procedure/src/main/java/org/apache/iotdb/procedure/env/ClusterProcedureEnvironment.java b/procedure/src/main/java/org/apache/iotdb/procedure/env/ClusterProcedureEnvironment.java deleted file mode 100644 index c8a8ad79ffe1..000000000000 --- a/procedure/src/main/java/org/apache/iotdb/procedure/env/ClusterProcedureEnvironment.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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.iotdb.procedure.env; - -/** - * Procedure Execute environment which is used to rpc between other nodes (datanode, confignode). - */ -public class ClusterProcedureEnvironment {} diff --git a/procedure/src/main/java/org/apache/iotdb/procedure/service/ProcedureNode.java b/procedure/src/main/java/org/apache/iotdb/procedure/service/ProcedureNode.java deleted file mode 100644 index 9627a9c986af..000000000000 --- a/procedure/src/main/java/org/apache/iotdb/procedure/service/ProcedureNode.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * 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.iotdb.procedure.service; - -import org.apache.iotdb.commons.exception.ShutdownException; -import org.apache.iotdb.commons.exception.StartupException; -import org.apache.iotdb.commons.service.JMXService; -import org.apache.iotdb.commons.service.RegisterManager; -import org.apache.iotdb.procedure.conf.ProcedureNodeConstant; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; - -public class ProcedureNode implements ProcedureNodeMBean { - private static final Logger LOGGER = LoggerFactory.getLogger(ProcedureNode.class); - private final String mbeanName = - String.format( - "%s:%s=%s", - ProcedureNodeConstant.PROCEDURENODE_PACKAGE, - ProcedureNodeConstant.JMX_TYPE, - "ProcedureNode"); - private final RegisterManager registerManager = new RegisterManager(); - - private ProcedureNode() {} - - public static ProcedureNode getInstance() { - return ProcedureNodeHolder.INSTANCE; - } - - public static void main(String[] args) { - new ProcedureServerCommandLine().doMain(args); - } - - public void setUp() throws StartupException, IOException { - LOGGER.info("Setting up {}...", ProcedureNodeConstant.GLOBAL_NAME); - registerManager.register(new JMXService()); - JMXService.registerMBean(getInstance(), mbeanName); - ProcedureServer.getInstance().initSyncedServiceImpl(new ProcedureServerProcessor()); - registerManager.register(ProcedureServer.getInstance()); - LOGGER.info("Init rpc server success"); - } - - public void active() { - try { - setUp(); - } catch (StartupException | IOException e) { - LOGGER.error("Meet error while starting up.", e); - ProcedureServer.getInstance().stop(); - LOGGER.warn("Procedure executor has stopped."); - deactivate(); - return; - } - LOGGER.info("{} has started.", ProcedureNodeConstant.GLOBAL_NAME); - } - - public void deactivate() { - LOGGER.info("Deactivating {}...", ProcedureNodeConstant.GLOBAL_NAME); - registerManager.deregisterAll(); - JMXService.deregisterMBean(mbeanName); - LOGGER.info("{} is deactivated.", ProcedureNodeConstant.GLOBAL_NAME); - } - - public void shutdown() throws ShutdownException { - LOGGER.info("Deactivating {}...", ProcedureNodeConstant.GLOBAL_NAME); - registerManager.shutdownAll(); - JMXService.deregisterMBean(mbeanName); - LOGGER.info("{} is deactivated.", ProcedureNodeConstant.GLOBAL_NAME); - } - - public void stop() { - deactivate(); - } - - private static class ProcedureNodeHolder { - private static final ProcedureNode INSTANCE = new ProcedureNode(); - - private ProcedureNodeHolder() {} - } -} diff --git a/procedure/src/main/java/org/apache/iotdb/procedure/service/ProcedureServer.java b/procedure/src/main/java/org/apache/iotdb/procedure/service/ProcedureServer.java deleted file mode 100644 index 0e2ee7d484c7..000000000000 --- a/procedure/src/main/java/org/apache/iotdb/procedure/service/ProcedureServer.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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.iotdb.procedure.service; - -import org.apache.iotdb.commons.concurrent.ThreadName; -import org.apache.iotdb.commons.exception.runtime.RPCServiceException; -import org.apache.iotdb.commons.service.ServiceType; -import org.apache.iotdb.commons.service.ThriftService; -import org.apache.iotdb.commons.service.ThriftServiceThread; -import org.apache.iotdb.procedure.ProcedureExecutor; -import org.apache.iotdb.procedure.conf.ProcedureNodeConfig; -import org.apache.iotdb.procedure.conf.ProcedureNodeConfigDescriptor; -import org.apache.iotdb.procedure.env.ClusterProcedureEnvironment; -import org.apache.iotdb.procedure.scheduler.ProcedureScheduler; -import org.apache.iotdb.procedure.scheduler.SimpleProcedureScheduler; -import org.apache.iotdb.procedure.store.IProcedureStore; -import org.apache.iotdb.service.rpc.thrift.ProcedureService; - -public class ProcedureServer extends ThriftService implements ProcedureNodeMBean { - - private static final ProcedureNodeConfig conf = - ProcedureNodeConfigDescriptor.getInstance().getConf(); - - private ProcedureScheduler scheduler = new SimpleProcedureScheduler(); - private ClusterProcedureEnvironment env = new ClusterProcedureEnvironment(); - private IProcedureStore store; - private ProcedureExecutor executor; - - private ProcedureServerProcessor client; - - public ProcedureServer() { - executor = new ProcedureExecutor(env, store, scheduler); - client = new ProcedureServerProcessor(executor); - } - - public void initExecutor() { - executor.init(conf.getWorkerThreadsCoreSize()); - executor.startWorkers(); - store.setRunning(true); - } - - public void stop() { - store.cleanup(); - store.setRunning(false); - executor.stop(); - } - - public static ProcedureServer getInstance() { - return ProcedureServerHolder.INSTANCE; - } - - @Override - public ServiceType getID() { - return ServiceType.PROCEDURE_SERVICE; - } - - @Override - public void initTProcessor() - throws ClassNotFoundException, IllegalAccessException, InstantiationException { - this.processor = new ProcedureService.Processor<>(client); - super.initSyncedServiceImpl(this.client); - } - - @Override - public void initThriftServiceThread() - throws IllegalAccessException, InstantiationException, ClassNotFoundException { - try { - thriftServiceThread = - new ThriftServiceThread( - processor, - getID().getName(), - ThreadName.PROCEDURE_NODE_CLIENT.getName(), - getBindIP(), - getBindPort(), - conf.getRpcMaxConcurrentClientNum(), - conf.getThriftServerAwaitTimeForStopService(), - new ProcedureServiceHanlder(client), - conf.isRpcThriftCompressionEnabled()); - } catch (RPCServiceException e) { - throw new IllegalAccessException(e.getMessage()); - } - thriftServiceThread.setName(ThreadName.PROCEDURE_NODE_SERVER.getName()); - } - - @Override - public String getBindIP() { - return conf.getRpcAddress(); - } - - @Override - public int getBindPort() { - return conf.getRpcPort(); - } - - private static class ProcedureServerHolder { - public static final ProcedureServer INSTANCE = new ProcedureServer(); - - private ProcedureServerHolder() {} - } -} diff --git a/procedure/src/main/java/org/apache/iotdb/procedure/service/ProcedureServerCommandLine.java b/procedure/src/main/java/org/apache/iotdb/procedure/service/ProcedureServerCommandLine.java deleted file mode 100644 index 4a068bb59225..000000000000 --- a/procedure/src/main/java/org/apache/iotdb/procedure/service/ProcedureServerCommandLine.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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.iotdb.procedure.service; - -import org.apache.iotdb.commons.ServerCommandLine; -import org.apache.iotdb.commons.exception.StartupException; -import org.apache.iotdb.commons.service.StartupChecks; -import org.apache.iotdb.procedure.conf.ProcedureNodeConfigDescriptor; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class ProcedureServerCommandLine extends ServerCommandLine { - private static final Logger LOGGER = LoggerFactory.getLogger(ProcedureServerCommandLine.class); - - @Override - protected String getUsage() { - return null; - } - - @Override - protected int run(String[] args) { - try { - StartupChecks checks = new StartupChecks().withDefaultTest(); - checks.verify(); - ProcedureNodeConfigDescriptor.getInstance().checkConfig(); - ProcedureNode procedureNode = ProcedureNode.getInstance(); - procedureNode.active(); - } catch (StartupException e) { - LOGGER.info("Meet error when doing start checking", e); - return -1; - } - return 0; - } -} diff --git a/procedure/src/main/java/org/apache/iotdb/procedure/service/ProcedureServerProcessor.java b/procedure/src/main/java/org/apache/iotdb/procedure/service/ProcedureServerProcessor.java deleted file mode 100644 index fa8f2bc0dd21..000000000000 --- a/procedure/src/main/java/org/apache/iotdb/procedure/service/ProcedureServerProcessor.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * 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.iotdb.procedure.service; - -import org.apache.iotdb.procedure.Procedure; -import org.apache.iotdb.procedure.ProcedureExecutor; -import org.apache.iotdb.procedure.store.IProcedureFactory; -import org.apache.iotdb.service.rpc.thrift.ProcedureService; -import org.apache.iotdb.service.rpc.thrift.SubmitProcedureReq; - -import org.apache.thrift.TException; - -import java.io.IOException; -import java.nio.ByteBuffer; - -public class ProcedureServerProcessor implements ProcedureService.Iface { - private ProcedureExecutor executor; - private IProcedureFactory procedureFactory; - - private static final String PROC_NOT_FOUND = "Proc %d not found"; - private static final String PROC_ABORT = "Proc %d is aborted"; - private static final String PROC_ABORT_FAILED = "Abort %d failed"; - private static final String PROC_SUBMIT = "Proc is submitted"; - - public ProcedureServerProcessor() {} - - public ProcedureServerProcessor(ProcedureExecutor executor) { - this.executor = executor; - } - - public IProcedureFactory getProcedureFactory() { - return procedureFactory; - } - - public void setProcedureFactory(IProcedureFactory procedureFactory) { - this.procedureFactory = procedureFactory; - } - - @Override - public String query(long procId) { - Procedure procedure = executor.getResultOrProcedure(procId); - if (null != procedure) { - return procedure.toString(); - } else { - return String.format(PROC_NOT_FOUND, procId); - } - } - - @Override - public String abort(long procId) { - return executor.abort(procId) - ? String.format(PROC_ABORT, procId) - : String.format(PROC_ABORT_FAILED, procId); - } - - @Override - public long submit(SubmitProcedureReq req) throws TException { - byte[] procedureBody = req.getProcedureBody(); - long procId; - ByteBuffer byteBuffer = ByteBuffer.wrap(procedureBody); - Procedure procedure = null; - try { - procedure = procedureFactory.create(byteBuffer); - } catch (IOException e) { - return -1; - } - procId = executor.submitProcedure(procedure); - return procId; - } -} diff --git a/procedure/src/main/java/org/apache/iotdb/procedure/service/ProcedureServiceHanlder.java b/procedure/src/main/java/org/apache/iotdb/procedure/service/ProcedureServiceHanlder.java deleted file mode 100644 index 6190cb4035f0..000000000000 --- a/procedure/src/main/java/org/apache/iotdb/procedure/service/ProcedureServiceHanlder.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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.iotdb.procedure.service; - -import org.apache.thrift.protocol.TProtocol; -import org.apache.thrift.server.ServerContext; -import org.apache.thrift.server.TServerEventHandler; -import org.apache.thrift.transport.TTransport; - -public class ProcedureServiceHanlder implements TServerEventHandler { - public ProcedureServiceHanlder(ProcedureServerProcessor client) {} - - @Override - public void preServe() {} - - @Override - public ServerContext createContext(TProtocol tProtocol, TProtocol tProtocol1) { - return null; - } - - @Override - public void deleteContext( - ServerContext serverContext, TProtocol tProtocol, TProtocol tProtocol1) {} - - @Override - public void processContext( - ServerContext serverContext, TTransport tTransport, TTransport tTransport1) {} -} diff --git a/procedure/src/test/java/org/apache/iotdb/procedure/service/TestProcedureService.java b/procedure/src/test/java/org/apache/iotdb/procedure/service/TestProcedureService.java deleted file mode 100644 index bac072b4219b..000000000000 --- a/procedure/src/test/java/org/apache/iotdb/procedure/service/TestProcedureService.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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.iotdb.procedure.service; - -import org.apache.iotdb.procedure.NoopProcedureStore; -import org.apache.iotdb.procedure.ProcedureExecutor; -import org.apache.iotdb.procedure.TestProcEnv; -import org.apache.iotdb.procedure.entity.IncProcedure; -import org.apache.iotdb.procedure.entity.TestProcedureFactory; -import org.apache.iotdb.procedure.scheduler.ProcedureScheduler; -import org.apache.iotdb.procedure.scheduler.SimpleProcedureScheduler; -import org.apache.iotdb.procedure.store.IProcedureStore; -import org.apache.iotdb.procedure.util.ProcedureTestUtil; -import org.apache.iotdb.service.rpc.thrift.SubmitProcedureReq; - -import org.apache.thrift.TException; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import java.io.IOException; -import java.nio.ByteBuffer; - -public class TestProcedureService { - - private int bufferSize = 16 * 1024 * 1024; - - ProcedureExecutor executor; - ProcedureServerProcessor client; - TestProcEnv env; - ProcedureScheduler scheduler; - IProcedureStore store; - - @Before - public void setUp() { - env = new TestProcEnv(); - scheduler = new SimpleProcedureScheduler(); - store = new NoopProcedureStore(); - executor = new ProcedureExecutor(env, store, scheduler); - client = new ProcedureServerProcessor(executor); - client.setProcedureFactory(new TestProcedureFactory()); - executor.init(4); - store.start(); - scheduler.start(); - executor.startWorkers(); - } - - @Test - public void testSubmitAndQuery() throws IOException, TException { - IncProcedure inc = new IncProcedure(); - ByteBuffer byteBuffer = ByteBuffer.allocate(bufferSize); - inc.serialize(byteBuffer); - byteBuffer.flip(); - byte[] bytes = new byte[byteBuffer.limit() - byteBuffer.position()]; - byteBuffer.get(bytes); - SubmitProcedureReq submitProcedureReq = new SubmitProcedureReq(); - submitProcedureReq.setProcedureBody(bytes); - byteBuffer.flip(); - long procId = client.submit(submitProcedureReq); - ProcedureTestUtil.waitForProcedure(executor, procId); - String query = client.query(procId); - Assert.assertTrue(query.contains("pid=1")); - } - - @After - public void tearDown() { - executor.stop(); - store.stop(); - scheduler.stop(); - } -} diff --git a/thrift-procedure/pom.xml b/thrift-procedure/pom.xml deleted file mode 100644 index ca3e5a3b7c7d..000000000000 --- a/thrift-procedure/pom.xml +++ /dev/null @@ -1,66 +0,0 @@ - - - - 4.0.0 - - org.apache.iotdb - iotdb-parent - 0.14.0-SNAPSHOT - ../pom.xml - - iotdb-thrift-procedure - rpc-thrift-procedure - - - org.apache.thrift - libthrift - - - org.apache.iotdb - iotdb-thrift - ${project.version} - - - - - - org.codehaus.mojo - build-helper-maven-plugin - 3.2.0 - - - add-source - generate-sources - - add-source - - - - ${project.build.directory}/generated-sources/thrift - - - - - - - - diff --git a/thrift-procedure/src/main/thrift/procedure.thrift b/thrift-procedure/src/main/thrift/procedure.thrift deleted file mode 100644 index 8c91bd8d41a9..000000000000 --- a/thrift-procedure/src/main/thrift/procedure.thrift +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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. - */ - -namespace java org.apache.iotdb.service.rpc.thrift -namespace py iotdb.thrift.rpc - -enum ProcedureState { - INITIALIZING = 1, - RUNNABLE = 2, - WAITING = 3, - WAITING_TIMEOUT = 4, - ROLLEDBACK = 5, - SUCCESS = 6, - FAILED = 7 -} - -struct SubmitProcedureReq{ - 1: optional binary procedureBody -} - -service ProcedureService{ - string query(i64 procId); - string abort(i64 procId); - i64 submit(SubmitProcedureReq req); - -} \ No newline at end of file From 06b96322f0b4b2dc1a6db8e5f89cb20accbe8efa Mon Sep 17 00:00:00 2001 From: Steve Yurong Su Date: Mon, 16 May 2022 15:44:59 +0800 Subject: [PATCH 019/436] [MPP][Bug hotfix] Support expressions in select and filter causes (#5918) * reused TypeProvider in converting Expression->Transformer stage * bind transformer input column with operator column input index * fix FilterOperator#iterateFilterReaderToNextValid() * invoke declarePositions(rowCount) in Operator#next() * invoke readyForFirstIteration() in hasNext() instead of in constructor * infer types in server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analyzer.java * bind input types * fix outputPointReaders construction --- .../operator/process/FilterOperator.java | 25 +++- .../operator/process/TransformOperator.java | 45 ++++--- .../iotdb/db/mpp/plan/analyze/Analyzer.java | 1 + .../plan/planner/LocalExecutionPlanner.java | 16 ++- .../iotdb/db/query/expression/Expression.java | 17 +++ .../expression/binary/BinaryExpression.java | 64 +++++++++- .../expression/leaf/ConstantOperand.java | 26 ++++ .../expression/leaf/TimeSeriesOperand.java | 38 ++++++ .../expression/leaf/TimestampOperand.java | 33 +++++ .../expression/multi/FunctionExpression.java | 115 ++++++++++++++++++ .../expression/unary/UnaryExpression.java | 50 ++++++++ .../query/udf/core/executor/UDTFExecutor.java | 27 ++++ .../udf/core/layer/EvaluationDAGBuilder.java | 25 +++- 13 files changed, 452 insertions(+), 30 deletions(-) diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/FilterOperator.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/FilterOperator.java index 79e5e2fd2ffa..8522d065cc83 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/FilterOperator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/FilterOperator.java @@ -23,6 +23,7 @@ import org.apache.iotdb.db.mpp.execution.operator.Operator; import org.apache.iotdb.db.mpp.execution.operator.OperatorContext; import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; +import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.InputLocation; import org.apache.iotdb.db.query.expression.Expression; import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; import org.apache.iotdb.tsfile.exception.write.UnSupportedDataTypeException; @@ -36,6 +37,7 @@ import java.time.ZoneId; import java.util.ArrayList; import java.util.List; +import java.util.Map; public class FilterOperator extends TransformOperator { @@ -45,6 +47,7 @@ public FilterOperator( OperatorContext operatorContext, Operator inputOperator, List inputDataTypes, + Map> inputLocations, Expression filterExpression, Expression[] outputExpressions, boolean keepNull, @@ -55,6 +58,7 @@ public FilterOperator( operatorContext, inputOperator, inputDataTypes, + inputLocations, bindExpressions(filterExpression, outputExpressions), keepNull, zoneId, @@ -70,8 +74,12 @@ private static Expression[] bindExpressions( } @Override - protected void initTransformers() throws QueryProcessException, IOException { - super.initTransformers(); + protected void initTransformers( + Map> inputLocations, + Expression[] outputExpressions, + TypeProvider typeProvider) + throws QueryProcessException, IOException { + super.initTransformers(inputLocations, outputExpressions, typeProvider); filterPointReader = transformers[transformers.length - 1]; if (filterPointReader.getDataType() != TSDataType.BOOLEAN) { @@ -88,10 +96,10 @@ protected void readyForFirstIteration() throws QueryProcessException, IOExceptio } private void iterateFilterReaderToNextValid() throws QueryProcessException, IOException { - while (filterPointReader.next() - && (filterPointReader.isCurrentNull() || !filterPointReader.currentBoolean())) { + do { filterPointReader.readyForNext(); - } + } while (filterPointReader.next() + && (filterPointReader.isCurrentNull() || !filterPointReader.currentBoolean())); } @Override @@ -135,6 +143,8 @@ public TsBlock next() { inputLayer.updateRowRecordListEvictionUpperBound(); } + + tsBlockBuilder.declarePositions(rowCount); } catch (Exception e) { throw new RuntimeException(e); } @@ -153,6 +163,11 @@ private long iterateValueReadersToNextValid(LayerPointReader reader, long curren @Override public boolean hasNext() { try { + if (isFirstIteration) { + readyForFirstIteration(); + isFirstIteration = false; + } + return filterPointReader.next(); } catch (Exception e) { throw new RuntimeException(e); diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/TransformOperator.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/TransformOperator.java index 7d47611f6d7e..784573cd1bbf 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/TransformOperator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/TransformOperator.java @@ -24,6 +24,7 @@ import org.apache.iotdb.db.mpp.execution.operator.Operator; import org.apache.iotdb.db.mpp.execution.operator.OperatorContext; import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; +import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.InputLocation; import org.apache.iotdb.db.query.expression.Expression; import org.apache.iotdb.db.query.udf.core.executor.UDTFContext; import org.apache.iotdb.db.query.udf.core.layer.EvaluationDAGBuilder; @@ -46,6 +47,7 @@ import java.time.ZoneId; import java.util.ArrayList; import java.util.List; +import java.util.Map; public class TransformOperator implements ProcessOperator { @@ -61,10 +63,10 @@ public class TransformOperator implements ProcessOperator { protected final OperatorContext operatorContext; protected final Operator inputOperator; - protected final List inputDataTypes; - protected final Expression[] outputExpressions; protected final boolean keepNull; + protected boolean isFirstIteration; + protected RawQueryInputLayer inputLayer; protected UDTFContext udtfContext; protected LayerPointReader[] transformers; @@ -75,6 +77,7 @@ public TransformOperator( OperatorContext operatorContext, Operator inputOperator, List inputDataTypes, + Map> inputLocations, Expression[] outputExpressions, boolean keepNull, ZoneId zoneId, @@ -82,15 +85,13 @@ public TransformOperator( throws QueryProcessException, IOException { this.operatorContext = operatorContext; this.inputOperator = inputOperator; - this.inputDataTypes = inputDataTypes; - this.outputExpressions = outputExpressions; this.keepNull = keepNull; + isFirstIteration = true; + initInputLayer(inputDataTypes); - initUdtfContext(zoneId); - initTransformers(); - readyForFirstIteration(); - updateTypeProvider(typeProvider); + initUdtfContext(outputExpressions, zoneId); + initTransformers(inputLocations, outputExpressions, typeProvider); } private void initInputLayer(List inputDataTypes) throws QueryProcessException { @@ -101,12 +102,16 @@ private void initInputLayer(List inputDataTypes) throws QueryProcess new TsBlockInputDataSet(inputOperator, inputDataTypes)); } - private void initUdtfContext(ZoneId zoneId) { + private void initUdtfContext(Expression[] outputExpressions, ZoneId zoneId) { udtfContext = new UDTFContext(zoneId); udtfContext.constructUdfExecutors(outputExpressions); } - protected void initTransformers() throws QueryProcessException, IOException { + protected void initTransformers( + Map> inputLocations, + Expression[] outputExpressions, + TypeProvider typeProvider) + throws QueryProcessException, IOException { UDFRegistrationService.getInstance().acquireRegistrationLock(); try { // This statement must be surrounded by the registration lock. @@ -116,10 +121,13 @@ protected void initTransformers() throws QueryProcessException, IOException { new EvaluationDAGBuilder( operatorContext.getOperatorId(), inputLayer, + inputLocations, outputExpressions, + typeProvider, udtfContext, udfTransformerMemoryBudgetInMB + udfCollectorMemoryBudgetInMB) .buildLayerMemoryAssigner() + .bindInputLayerColumnIndexWithExpression() .buildResultColumnPointReaders() .getOutputPointReaders(); } finally { @@ -149,14 +157,17 @@ private void iterateReaderToNextValid(LayerPointReader reader) } } - private void updateTypeProvider(TypeProvider typeProvider) { - for (int i = 0; i < transformers.length; ++i) { - typeProvider.setType(outputExpressions[i].toString(), transformers[i].getDataType()); - } - } - @Override public boolean hasNext() { + if (isFirstIteration) { + try { + readyForFirstIteration(); + } catch (Exception e) { + throw new RuntimeException(e); + } + isFirstIteration = false; + } + return !timeHeap.isEmpty(); } @@ -195,6 +206,8 @@ public TsBlock next() { inputLayer.updateRowRecordListEvictionUpperBound(); } + + tsBlockBuilder.declarePositions(rowCount); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analyzer.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analyzer.java index fd6053fd209f..97054bb3cd4d 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analyzer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analyzer.java @@ -342,6 +342,7 @@ private List> analyzeSelect( alias = hasAlias ? resultColumn.getAlias() : alias; outputExpressions.add(new Pair<>(expressionWithoutAlias, alias)); ExpressionAnalyzer.updateTypeProvider(expressionWithoutAlias, typeProvider); + expressionWithoutAlias.inferTypes(typeProvider); paginationController.consumeLimit(); } else { break; diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LocalExecutionPlanner.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LocalExecutionPlanner.java index 3273638f0466..6a8c5956e218 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LocalExecutionPlanner.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LocalExecutionPlanner.java @@ -454,13 +454,15 @@ public Operator visitTransform(TransformNode node, LocalExecutionPlanContext con node.getPlanNodeId(), TransformNode.class.getSimpleName()); final Operator inputOperator = generateOnlyChildOperator(node, context); - final List inputDataTypes = getOutputColumnTypes(node, context.getTypeProvider()); + final List inputDataTypes = getInputColumnTypes(node, context.getTypeProvider()); + final Map> inputLocations = makeLayout(node); try { return new TransformOperator( operatorContext, inputOperator, inputDataTypes, + inputLocations, node.getOutputExpressions(), node.isKeepNull(), node.getZoneId(), @@ -476,13 +478,15 @@ public Operator visitFilter(FilterNode node, LocalExecutionPlanContext context) context.instanceContext.addOperatorContext( context.getNextOperatorId(), node.getPlanNodeId(), FilterNode.class.getSimpleName()); final Operator inputOperator = generateOnlyChildOperator(node, context); - final List inputDataTypes = getOutputColumnTypes(node, context.getTypeProvider()); + final List inputDataTypes = getInputColumnTypes(node, context.getTypeProvider()); + final Map> inputLocations = makeLayout(node); try { return new FilterOperator( operatorContext, inputOperator, inputDataTypes, + inputLocations, node.getPredicate(), node.getOutputExpressions(), node.isKeepNull(), @@ -676,6 +680,14 @@ private Map> makeLayout(PlanNode node) { return outputMappings; } + private List getInputColumnTypes(PlanNode node, TypeProvider typeProvider) { + return node.getChildren().stream() + .map(PlanNode::getOutputColumnNames) + .flatMap(List::stream) + .map(typeProvider::getType) + .collect(Collectors.toList()); + } + private List getOutputColumnTypes(PlanNode node, TypeProvider typeProvider) { return node.getOutputColumnNames().stream() .map(typeProvider::getType) diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/Expression.java b/server/src/main/java/org/apache/iotdb/db/query/expression/Expression.java index 4dceb6106ea6..28698ab1335b 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/Expression.java +++ b/server/src/main/java/org/apache/iotdb/db/query/expression/Expression.java @@ -24,6 +24,7 @@ import org.apache.iotdb.db.exception.query.QueryProcessException; import org.apache.iotdb.db.exception.sql.SemanticException; import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; +import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.InputLocation; import org.apache.iotdb.db.qp.physical.crud.UDTFPlan; import org.apache.iotdb.db.query.expression.binary.AdditionExpression; import org.apache.iotdb.db.query.expression.binary.DivisionExpression; @@ -135,10 +136,17 @@ protected static void checkInputExpressionDataType( protected Integer inputColumnIndex = null; + // TODO: remove after MPP finish + @Deprecated public abstract void bindInputLayerColumnIndexWithExpression(UDTFPlan udtfPlan); + public abstract void bindInputLayerColumnIndexWithExpression( + Map> inputLocations); + public abstract void updateStatisticsForMemoryAssigner(LayerMemoryAssigner memoryAssigner); + // TODO: remove after MPP finish + @Deprecated public abstract IntermediateLayer constructIntermediateLayer( long queryId, UDTFContext udtfContext, @@ -148,6 +156,15 @@ public abstract IntermediateLayer constructIntermediateLayer( LayerMemoryAssigner memoryAssigner) throws QueryProcessException, IOException; + public abstract IntermediateLayer constructIntermediateLayer( + long queryId, + UDTFContext udtfContext, + RawQueryInputLayer rawTimeSeriesInputLayer, + Map expressionIntermediateLayerMap, + TypeProvider typeProvider, + LayerMemoryAssigner memoryAssigner) + throws QueryProcessException, IOException; + ///////////////////////////////////////////////////////////////////////////////////////////////// // isConstantOperand ///////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/BinaryExpression.java b/server/src/main/java/org/apache/iotdb/db/query/expression/binary/BinaryExpression.java index d9a5836d9b88..6dac04bf2983 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/BinaryExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/query/expression/binary/BinaryExpression.java @@ -22,6 +22,8 @@ import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.exception.query.LogicalOptimizeException; import org.apache.iotdb.db.exception.query.QueryProcessException; +import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; +import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.InputLocation; import org.apache.iotdb.db.qp.physical.crud.UDTFPlan; import org.apache.iotdb.db.query.expression.Expression; import org.apache.iotdb.db.query.udf.core.executor.UDTFContext; @@ -191,12 +193,24 @@ public void constructUdfExecutors( } @Override - public void bindInputLayerColumnIndexWithExpression(UDTFPlan udtfPlan) { + public final void bindInputLayerColumnIndexWithExpression(UDTFPlan udtfPlan) { leftExpression.bindInputLayerColumnIndexWithExpression(udtfPlan); rightExpression.bindInputLayerColumnIndexWithExpression(udtfPlan); inputColumnIndex = udtfPlan.getReaderIndexByExpressionName(toString()); } + @Override + public final void bindInputLayerColumnIndexWithExpression( + Map> inputLocations) { + leftExpression.bindInputLayerColumnIndexWithExpression(inputLocations); + rightExpression.bindInputLayerColumnIndexWithExpression(inputLocations); + + final String digest = toString(); + if (inputLocations.containsKey(digest)) { + inputColumnIndex = inputLocations.get(digest).get(0).getValueColumnIndex(); + } + } + @Override public void updateStatisticsForMemoryAssigner(LayerMemoryAssigner memoryAssigner) { leftExpression.updateStatisticsForMemoryAssigner(memoryAssigner); @@ -253,6 +267,54 @@ public IntermediateLayer constructIntermediateLayer( return expressionIntermediateLayerMap.get(this); } + @Override + public IntermediateLayer constructIntermediateLayer( + long queryId, + UDTFContext udtfContext, + RawQueryInputLayer rawTimeSeriesInputLayer, + Map expressionIntermediateLayerMap, + TypeProvider typeProvider, + LayerMemoryAssigner memoryAssigner) + throws QueryProcessException, IOException { + if (!expressionIntermediateLayerMap.containsKey(this)) { + float memoryBudgetInMB = memoryAssigner.assign(); + + IntermediateLayer leftParentIntermediateLayer = + leftExpression.constructIntermediateLayer( + queryId, + udtfContext, + rawTimeSeriesInputLayer, + expressionIntermediateLayerMap, + typeProvider, + memoryAssigner); + IntermediateLayer rightParentIntermediateLayer = + rightExpression.constructIntermediateLayer( + queryId, + udtfContext, + rawTimeSeriesInputLayer, + expressionIntermediateLayerMap, + typeProvider, + memoryAssigner); + Transformer transformer = + constructTransformer( + leftParentIntermediateLayer.constructPointReader(), + rightParentIntermediateLayer.constructPointReader()); + + // SingleInputColumnMultiReferenceIntermediateLayer doesn't support ConstantLayerPointReader + // yet. And since a ConstantLayerPointReader won't produce too much IO, + // SingleInputColumnSingleReferenceIntermediateLayer could be a better choice. + expressionIntermediateLayerMap.put( + this, + memoryAssigner.getReference(this) == 1 || isConstantOperand() + ? new SingleInputColumnSingleReferenceIntermediateLayer( + this, queryId, memoryBudgetInMB, transformer) + : new SingleInputColumnMultiReferenceIntermediateLayer( + this, queryId, memoryBudgetInMB, transformer)); + } + + return expressionIntermediateLayerMap.get(this); + } + protected abstract BinaryTransformer constructTransformer( LayerPointReader leftParentLayerPointReader, LayerPointReader rightParentLayerPointReader); diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/leaf/ConstantOperand.java b/server/src/main/java/org/apache/iotdb/db/query/expression/leaf/ConstantOperand.java index 7acd877fc08e..8a6c4589c0d7 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/leaf/ConstantOperand.java +++ b/server/src/main/java/org/apache/iotdb/db/query/expression/leaf/ConstantOperand.java @@ -22,6 +22,7 @@ import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.exception.query.QueryProcessException; import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; +import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.InputLocation; import org.apache.iotdb.db.qp.physical.crud.UDTFPlan; import org.apache.iotdb.db.query.expression.Expression; import org.apache.iotdb.db.query.expression.ExpressionType; @@ -35,6 +36,7 @@ import org.apache.commons.lang3.Validate; +import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import java.util.Map; @@ -103,6 +105,12 @@ public void bindInputLayerColumnIndexWithExpression(UDTFPlan udtfPlan) { // Do nothing } + @Override + public void bindInputLayerColumnIndexWithExpression( + Map> inputLocations) { + // Do nothing + } + @Override public void updateStatisticsForMemoryAssigner(LayerMemoryAssigner memoryAssigner) { // Do nothing @@ -127,6 +135,24 @@ public IntermediateLayer constructIntermediateLayer( return expressionIntermediateLayerMap.get(this); } + @Override + public IntermediateLayer constructIntermediateLayer( + long queryId, + UDTFContext udtfContext, + RawQueryInputLayer rawTimeSeriesInputLayer, + Map expressionIntermediateLayerMap, + TypeProvider typeProvider, + LayerMemoryAssigner memoryAssigner) + throws QueryProcessException, IOException { + if (!expressionIntermediateLayerMap.containsKey(this)) { + IntermediateLayer intermediateLayer = + new ConstantIntermediateLayer(this, queryId, memoryAssigner.assign()); + expressionIntermediateLayerMap.put(this, intermediateLayer); + } + + return expressionIntermediateLayerMap.get(this); + } + @Override public String getExpressionStringInternal() { return valueString; diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/leaf/TimeSeriesOperand.java b/server/src/main/java/org/apache/iotdb/db/query/expression/leaf/TimeSeriesOperand.java index 5d896a932f25..cdab2e4adc53 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/leaf/TimeSeriesOperand.java +++ b/server/src/main/java/org/apache/iotdb/db/query/expression/leaf/TimeSeriesOperand.java @@ -24,6 +24,7 @@ import org.apache.iotdb.db.exception.query.QueryProcessException; import org.apache.iotdb.db.metadata.path.PathDeserializeUtil; import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; +import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.InputLocation; import org.apache.iotdb.db.qp.physical.crud.UDTFPlan; import org.apache.iotdb.db.query.expression.Expression; import org.apache.iotdb.db.query.expression.ExpressionType; @@ -36,6 +37,7 @@ import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; +import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import java.util.Map; @@ -98,6 +100,15 @@ public void bindInputLayerColumnIndexWithExpression(UDTFPlan udtfPlan) { inputColumnIndex = udtfPlan.getReaderIndexByExpressionName(toString()); } + @Override + public void bindInputLayerColumnIndexWithExpression( + Map> inputLocations) { + final String digest = toString(); + if (inputLocations.containsKey(digest)) { + inputColumnIndex = inputLocations.get(digest).get(0).getValueColumnIndex(); + } + } + @Override public void updateStatisticsForMemoryAssigner(LayerMemoryAssigner memoryAssigner) { memoryAssigner.increaseExpressionReference(this); @@ -131,6 +142,33 @@ public IntermediateLayer constructIntermediateLayer( return expressionIntermediateLayerMap.get(this); } + @Override + public IntermediateLayer constructIntermediateLayer( + long queryId, + UDTFContext udtfContext, + RawQueryInputLayer rawTimeSeriesInputLayer, + Map expressionIntermediateLayerMap, + TypeProvider typeProvider, + LayerMemoryAssigner memoryAssigner) + throws QueryProcessException, IOException { + if (!expressionIntermediateLayerMap.containsKey(this)) { + float memoryBudgetInMB = memoryAssigner.assign(); + + LayerPointReader parentLayerPointReader = + rawTimeSeriesInputLayer.constructValuePointReader(inputColumnIndex); + + expressionIntermediateLayerMap.put( + this, + memoryAssigner.getReference(this) == 1 + ? new SingleInputColumnSingleReferenceIntermediateLayer( + this, queryId, memoryBudgetInMB, parentLayerPointReader) + : new SingleInputColumnMultiReferenceIntermediateLayer( + this, queryId, memoryBudgetInMB, parentLayerPointReader)); + } + + return expressionIntermediateLayerMap.get(this); + } + @Override public String getExpressionStringInternal() { return path.isMeasurementAliasExists() ? path.getFullPathWithAlias() : path.getFullPath(); diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/leaf/TimestampOperand.java b/server/src/main/java/org/apache/iotdb/db/query/expression/leaf/TimestampOperand.java index bb473451b753..8c9f6c741df9 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/leaf/TimestampOperand.java +++ b/server/src/main/java/org/apache/iotdb/db/query/expression/leaf/TimestampOperand.java @@ -23,6 +23,7 @@ import org.apache.iotdb.db.exception.query.LogicalOptimizeException; import org.apache.iotdb.db.exception.query.QueryProcessException; import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; +import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.InputLocation; import org.apache.iotdb.db.qp.physical.crud.UDTFPlan; import org.apache.iotdb.db.query.expression.Expression; import org.apache.iotdb.db.query.expression.ExpressionType; @@ -87,6 +88,12 @@ public void bindInputLayerColumnIndexWithExpression(UDTFPlan udtfPlan) { // do nothing } + @Override + public void bindInputLayerColumnIndexWithExpression( + Map> inputLocations) { + // do nothing + } + @Override public void updateStatisticsForMemoryAssigner(LayerMemoryAssigner memoryAssigner) { memoryAssigner.increaseExpressionReference(this); @@ -119,6 +126,32 @@ public IntermediateLayer constructIntermediateLayer( return expressionIntermediateLayerMap.get(this); } + @Override + public IntermediateLayer constructIntermediateLayer( + long queryId, + UDTFContext udtfContext, + RawQueryInputLayer rawTimeSeriesInputLayer, + Map expressionIntermediateLayerMap, + TypeProvider typeProvider, + LayerMemoryAssigner memoryAssigner) + throws QueryProcessException, IOException { + if (!expressionIntermediateLayerMap.containsKey(this)) { + float memoryBudgetInMB = memoryAssigner.assign(); + + LayerPointReader parentLayerPointReader = rawTimeSeriesInputLayer.constructTimePointReader(); + + expressionIntermediateLayerMap.put( + this, + memoryAssigner.getReference(this) == 1 + ? new SingleInputColumnSingleReferenceIntermediateLayer( + this, queryId, memoryBudgetInMB, parentLayerPointReader) + : new SingleInputColumnMultiReferenceIntermediateLayer( + this, queryId, memoryBudgetInMB, parentLayerPointReader)); + } + + return expressionIntermediateLayerMap.get(this); + } + @Override protected boolean isConstantOperandInternal() { return false; diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/multi/FunctionExpression.java b/server/src/main/java/org/apache/iotdb/db/query/expression/multi/FunctionExpression.java index 0ec226403f03..caa87859e6a4 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/multi/FunctionExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/query/expression/multi/FunctionExpression.java @@ -25,6 +25,7 @@ import org.apache.iotdb.db.exception.query.QueryProcessException; import org.apache.iotdb.db.exception.sql.SemanticException; import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; +import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.InputLocation; import org.apache.iotdb.db.qp.constant.SQLConstant; import org.apache.iotdb.db.qp.physical.crud.UDTFPlan; import org.apache.iotdb.db.qp.strategy.optimizer.ConcatPathOptimizer; @@ -279,6 +280,19 @@ public void bindInputLayerColumnIndexWithExpression(UDTFPlan udtfPlan) { inputColumnIndex = udtfPlan.getReaderIndexByExpressionName(toString()); } + @Override + public void bindInputLayerColumnIndexWithExpression( + Map> inputLocations) { + for (Expression expression : expressions) { + expression.bindInputLayerColumnIndexWithExpression(inputLocations); + } + + final String digest = toString(); + if (inputLocations.containsKey(digest)) { + inputColumnIndex = inputLocations.get(digest).get(0).getValueColumnIndex(); + } + } + @Override public void updateStatisticsForMemoryAssigner(LayerMemoryAssigner memoryAssigner) { for (Expression expression : expressions) { @@ -287,6 +301,103 @@ public void updateStatisticsForMemoryAssigner(LayerMemoryAssigner memoryAssigner } } + @Override + public IntermediateLayer constructIntermediateLayer( + long queryId, + UDTFContext udtfContext, + RawQueryInputLayer rawTimeSeriesInputLayer, + Map expressionIntermediateLayerMap, + TypeProvider typeProvider, + LayerMemoryAssigner memoryAssigner) + throws QueryProcessException, IOException { + if (!expressionIntermediateLayerMap.containsKey(this)) { + float memoryBudgetInMB = memoryAssigner.assign(); + Transformer transformer; + if (isBuiltInAggregationFunctionExpression) { + transformer = + new TransparentTransformer( + rawTimeSeriesInputLayer.constructValuePointReader(inputColumnIndex)); + } else { + IntermediateLayer udfInputIntermediateLayer = + constructUdfInputIntermediateLayer( + queryId, + udtfContext, + rawTimeSeriesInputLayer, + expressionIntermediateLayerMap, + typeProvider, + memoryAssigner); + transformer = + constructUdfTransformer( + queryId, udtfContext, typeProvider, memoryAssigner, udfInputIntermediateLayer); + } + expressionIntermediateLayerMap.put( + this, + memoryAssigner.getReference(this) == 1 + ? new SingleInputColumnSingleReferenceIntermediateLayer( + this, queryId, memoryBudgetInMB, transformer) + : new SingleInputColumnMultiReferenceIntermediateLayer( + this, queryId, memoryBudgetInMB, transformer)); + } + + return expressionIntermediateLayerMap.get(this); + } + + private IntermediateLayer constructUdfInputIntermediateLayer( + long queryId, + UDTFContext udtfContext, + RawQueryInputLayer rawTimeSeriesInputLayer, + Map expressionIntermediateLayerMap, + TypeProvider typeProvider, + LayerMemoryAssigner memoryAssigner) + throws QueryProcessException, IOException { + List intermediateLayers = new ArrayList<>(); + for (Expression expression : expressions) { + intermediateLayers.add( + expression.constructIntermediateLayer( + queryId, + udtfContext, + rawTimeSeriesInputLayer, + expressionIntermediateLayerMap, + typeProvider, + memoryAssigner)); + } + return intermediateLayers.size() == 1 + ? intermediateLayers.get(0) + : new MultiInputColumnIntermediateLayer( + this, + queryId, + memoryAssigner.assign(), + intermediateLayers.stream() + .map(IntermediateLayer::constructPointReader) + .collect(Collectors.toList())); + } + + private UDFQueryTransformer constructUdfTransformer( + long queryId, + UDTFContext udtfContext, + TypeProvider typeProvider, + LayerMemoryAssigner memoryAssigner, + IntermediateLayer udfInputIntermediateLayer) + throws QueryProcessException, IOException { + UDTFExecutor executor = udtfContext.getExecutorByFunctionExpression(this); + + executor.beforeStart(queryId, memoryAssigner.assign(), typeProvider); + + AccessStrategy accessStrategy = executor.getConfigurations().getAccessStrategy(); + switch (accessStrategy.getAccessStrategyType()) { + case ROW_BY_ROW: + return new UDFQueryRowTransformer(udfInputIntermediateLayer.constructRowReader(), executor); + case SLIDING_SIZE_WINDOW: + case SLIDING_TIME_WINDOW: + return new UDFQueryRowWindowTransformer( + udfInputIntermediateLayer.constructRowWindowReader( + accessStrategy, memoryAssigner.assign()), + executor); + default: + throw new UnsupportedOperationException("Unsupported transformer access strategy"); + } + } + @Override public IntermediateLayer constructIntermediateLayer( long queryId, @@ -333,6 +444,8 @@ public IntermediateLayer constructIntermediateLayer( return expressionIntermediateLayerMap.get(this); } + // TODO: remove it after MPP finished + @Deprecated private IntermediateLayer constructUdfInputIntermediateLayer( long queryId, UDTFContext udtfContext, @@ -363,6 +476,8 @@ private IntermediateLayer constructUdfInputIntermediateLayer( .collect(Collectors.toList())); } + // TODO: remove it after MPP finished + @Deprecated private UDFQueryTransformer constructUdfTransformer( long queryId, UDTFContext udtfContext, diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/unary/UnaryExpression.java b/server/src/main/java/org/apache/iotdb/db/query/expression/unary/UnaryExpression.java index f82d980e7c72..02ec49a4b129 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/unary/UnaryExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/query/expression/unary/UnaryExpression.java @@ -22,6 +22,8 @@ import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.exception.query.LogicalOptimizeException; import org.apache.iotdb.db.exception.query.QueryProcessException; +import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; +import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.InputLocation; import org.apache.iotdb.db.qp.physical.crud.UDTFPlan; import org.apache.iotdb.db.qp.utils.WildcardsRemover; import org.apache.iotdb.db.query.expression.Expression; @@ -95,6 +97,17 @@ public final void bindInputLayerColumnIndexWithExpression(UDTFPlan udtfPlan) { inputColumnIndex = udtfPlan.getReaderIndexByExpressionName(toString()); } + @Override + public final void bindInputLayerColumnIndexWithExpression( + Map> inputLocations) { + expression.bindInputLayerColumnIndexWithExpression(inputLocations); + + final String digest = toString(); + if (inputLocations.containsKey(digest)) { + inputColumnIndex = inputLocations.get(digest).get(0).getValueColumnIndex(); + } + } + @Override public final void updateStatisticsForMemoryAssigner(LayerMemoryAssigner memoryAssigner) { expression.updateStatisticsForMemoryAssigner(memoryAssigner); @@ -139,6 +152,43 @@ public final IntermediateLayer constructIntermediateLayer( return expressionIntermediateLayerMap.get(this); } + @Override + public IntermediateLayer constructIntermediateLayer( + long queryId, + UDTFContext udtfContext, + RawQueryInputLayer rawTimeSeriesInputLayer, + Map expressionIntermediateLayerMap, + TypeProvider typeProvider, + LayerMemoryAssigner memoryAssigner) + throws QueryProcessException, IOException { + if (!expressionIntermediateLayerMap.containsKey(this)) { + float memoryBudgetInMB = memoryAssigner.assign(); + + IntermediateLayer parentLayerPointReader = + expression.constructIntermediateLayer( + queryId, + udtfContext, + rawTimeSeriesInputLayer, + expressionIntermediateLayerMap, + typeProvider, + memoryAssigner); + Transformer transformer = constructTransformer(parentLayerPointReader.constructPointReader()); + + // SingleInputColumnMultiReferenceIntermediateLayer doesn't support ConstantLayerPointReader + // yet. And since a ConstantLayerPointReader won't produce too much IO, + // SingleInputColumnSingleReferenceIntermediateLayer could be a better choice. + expressionIntermediateLayerMap.put( + this, + memoryAssigner.getReference(this) == 1 || isConstantOperand() + ? new SingleInputColumnSingleReferenceIntermediateLayer( + this, queryId, memoryBudgetInMB, transformer) + : new SingleInputColumnMultiReferenceIntermediateLayer( + this, queryId, memoryBudgetInMB, transformer)); + } + + return expressionIntermediateLayerMap.get(this); + } + protected abstract Transformer constructTransformer(LayerPointReader pointReader); @Override diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/executor/UDTFExecutor.java b/server/src/main/java/org/apache/iotdb/db/query/udf/core/executor/UDTFExecutor.java index 83cb5990a071..b8f697f6b77b 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/executor/UDTFExecutor.java +++ b/server/src/main/java/org/apache/iotdb/db/query/udf/core/executor/UDTFExecutor.java @@ -20,6 +20,7 @@ package org.apache.iotdb.db.query.udf.core.executor; import org.apache.iotdb.db.exception.query.QueryProcessException; +import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; import org.apache.iotdb.db.query.expression.Expression; import org.apache.iotdb.db.query.expression.multi.FunctionExpression; import org.apache.iotdb.db.query.udf.api.UDTF; @@ -53,6 +54,32 @@ public UDTFExecutor(FunctionExpression expression, ZoneId zoneId) { configurations = new UDTFConfigurations(zoneId); } + public void beforeStart(long queryId, float collectorMemoryBudgetInMB, TypeProvider typeProvider) + throws QueryProcessException { + udtf = (UDTF) UDFRegistrationService.getInstance().reflect(expression); + + UDFParameters parameters = new UDFParameters(expression, typeProvider); + + try { + udtf.validate(new UDFParameterValidator(parameters)); + } catch (Exception e) { + onError("validate(UDFParameterValidator)", e); + } + + try { + udtf.beforeStart(parameters, configurations); + } catch (Exception e) { + onError("beforeStart(UDFParameters, UDTFConfigurations)", e); + } + configurations.check(); + + collector = + ElasticSerializableTVList.newElasticSerializableTVList( + configurations.getOutputDataType(), queryId, collectorMemoryBudgetInMB, 1); + } + + // TODO: remove it after MPP finished + @Deprecated public void beforeStart( long queryId, float collectorMemoryBudgetInMB, diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/EvaluationDAGBuilder.java b/server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/EvaluationDAGBuilder.java index debc89d25032..d083191b34da 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/EvaluationDAGBuilder.java +++ b/server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/EvaluationDAGBuilder.java @@ -20,13 +20,15 @@ package org.apache.iotdb.db.query.udf.core.layer; import org.apache.iotdb.db.exception.query.QueryProcessException; +import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; +import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.InputLocation; import org.apache.iotdb.db.query.expression.Expression; import org.apache.iotdb.db.query.udf.core.executor.UDTFContext; import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; -import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.io.IOException; import java.util.HashMap; +import java.util.List; import java.util.Map; public class EvaluationDAGBuilder { @@ -34,10 +36,13 @@ public class EvaluationDAGBuilder { private final long queryId; private final RawQueryInputLayer inputLayer; + private final Map> inputLocations; private final Expression[] outputExpressions; private final LayerPointReader[] outputPointReaders; + private final TypeProvider typeProvider; + private final UDTFContext udtfContext; private final LayerMemoryAssigner memoryAssigner; @@ -47,26 +52,27 @@ public class EvaluationDAGBuilder { // sub-expressions, but they can share the same point reader. we cache the point reader here to // make sure that only one point reader will be built for one expression. private final Map expressionIntermediateLayerMap; - private final Map expressionDataTypeMap; public EvaluationDAGBuilder( long queryId, RawQueryInputLayer inputLayer, + Map> inputLocations, Expression[] outputExpressions, + TypeProvider typeProvider, UDTFContext udtfContext, float memoryBudgetInMB) { this.queryId = queryId; this.inputLayer = inputLayer; + this.inputLocations = inputLocations; this.outputExpressions = outputExpressions; + this.typeProvider = typeProvider; this.udtfContext = udtfContext; - int size = inputLayer.getInputColumnCount(); - outputPointReaders = new LayerPointReader[size]; + outputPointReaders = new LayerPointReader[outputExpressions.length]; memoryAssigner = new LayerMemoryAssigner(memoryBudgetInMB); expressionIntermediateLayerMap = new HashMap<>(); - expressionDataTypeMap = new HashMap<>(); } public EvaluationDAGBuilder buildLayerMemoryAssigner() { @@ -77,6 +83,13 @@ public EvaluationDAGBuilder buildLayerMemoryAssigner() { return this; } + public EvaluationDAGBuilder bindInputLayerColumnIndexWithExpression() { + for (Expression expression : outputExpressions) { + expression.bindInputLayerColumnIndexWithExpression(inputLocations); + } + return this; + } + public EvaluationDAGBuilder buildResultColumnPointReaders() throws QueryProcessException, IOException { for (int i = 0; i < outputExpressions.length; ++i) { @@ -87,7 +100,7 @@ public EvaluationDAGBuilder buildResultColumnPointReaders() udtfContext, inputLayer, expressionIntermediateLayerMap, - expressionDataTypeMap, + typeProvider, memoryAssigner) .constructPointReader(); } From 2d4b5bc1acb5c06c8bc3556aff1900ea17f3c31c Mon Sep 17 00:00:00 2001 From: YongzaoDan <33111881+CRZbulabula@users.noreply.github.com> Date: Mon, 16 May 2022 17:49:17 +0800 Subject: [PATCH 020/436] [IOTDB-2689] [IOTDB-2690] Simple Partition load balancing (#5910) --- .../confignode/manager/PartitionManager.java | 57 +++++++--- .../confignode/persistence/PartitionInfo.java | 101 ++++++++++++++---- .../persistence/PartitionInfoTest.java | 54 ++++++++-- 3 files changed, 167 insertions(+), 45 deletions(-) diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/PartitionManager.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/PartitionManager.java index ee6e6f35202d..5d90ab1551f7 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/PartitionManager.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/PartitionManager.java @@ -42,15 +42,16 @@ import org.apache.iotdb.consensus.common.DataSet; import org.apache.iotdb.consensus.common.response.ConsensusReadResponse; import org.apache.iotdb.rpc.TSStatusCode; +import org.apache.iotdb.tsfile.utils.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Random; /** The PartitionManager Manages cluster PartitionTable read and write requests. */ public class PartitionManager { @@ -128,23 +129,37 @@ public DataSet getOrCreateSchemaPartition(GetOrCreateSchemaPartitionReq physical */ private Map> allocateSchemaPartition( Map> noAssignedSchemaPartitionSlotsMap) { + Map> result = new HashMap<>(); + Map regionReplicaMap = + partitionInfo.getRegionReplicaMap(); for (String storageGroup : noAssignedSchemaPartitionSlotsMap.keySet()) { + List noAssignedPartitionSlots = noAssignedSchemaPartitionSlotsMap.get(storageGroup); - List schemaRegionReplicaSets = - partitionInfo.getRegionReplicaSets( + // List> + List> regionSlotsCounter = + partitionInfo.getSortedRegionSlotsCounter( getClusterSchemaManager() .getRegionGroupIds(storageGroup, TConsensusGroupType.SchemaRegion)); - Random random = new Random(); Map allocateResult = new HashMap<>(); - noAssignedPartitionSlots.forEach( - seriesPartitionSlot -> - allocateResult.put( - seriesPartitionSlot, - schemaRegionReplicaSets.get(random.nextInt(schemaRegionReplicaSets.size())))); + for (TSeriesPartitionSlot seriesPartitionSlot : noAssignedPartitionSlots) { + // Do greedy allocation + Pair bestRegion = regionSlotsCounter.get(0); + allocateResult.put(seriesPartitionSlot, regionReplicaMap.get(bestRegion.getRight())); + + // Bubble sort + int index = 0; + regionSlotsCounter.set(0, new Pair<>(bestRegion.getLeft() + 1, bestRegion.getRight())); + while (index < regionSlotsCounter.size() - 1 + && regionSlotsCounter.get(index).getLeft() + > regionSlotsCounter.get(index + 1).getLeft()) { + Collections.swap(regionSlotsCounter, index, index + 1); + index += 1; + } + } result.put(storageGroup, allocateResult); } @@ -220,15 +235,18 @@ public DataSet getOrCreateDataPartition(GetOrCreateDataPartitionReq physicalPlan Map>>> result = new HashMap<>(); + Map regionReplicaMap = + partitionInfo.getRegionReplicaMap(); for (String storageGroup : noAssignedDataPartitionSlotsMap.keySet()) { + Map> noAssignedPartitionSlotsMap = noAssignedDataPartitionSlotsMap.get(storageGroup); - List dataRegionEndPoints = - partitionInfo.getRegionReplicaSets( + // List> + List> regionSlotsCounter = + partitionInfo.getSortedRegionSlotsCounter( getClusterSchemaManager() .getRegionGroupIds(storageGroup, TConsensusGroupType.DataRegion)); - Random random = new Random(); Map>> allocateResult = new HashMap<>(); @@ -236,10 +254,23 @@ public DataSet getOrCreateDataPartition(GetOrCreateDataPartitionReq physicalPlan noAssignedPartitionSlotsMap.entrySet()) { allocateResult.put(seriesPartitionEntry.getKey(), new HashMap<>()); for (TTimePartitionSlot timePartitionSlot : seriesPartitionEntry.getValue()) { + + // Do greedy allocation + Pair bestRegion = regionSlotsCounter.get(0); allocateResult .get(seriesPartitionEntry.getKey()) .computeIfAbsent(timePartitionSlot, key -> new ArrayList<>()) - .add(dataRegionEndPoints.get(random.nextInt(dataRegionEndPoints.size()))); + .add(regionReplicaMap.get(bestRegion.getRight())); + + // Bubble sort + int index = 0; + regionSlotsCounter.set(0, new Pair<>(bestRegion.getLeft() + 1, bestRegion.getRight())); + while (index < regionSlotsCounter.size() - 1 + && regionSlotsCounter.get(index).getLeft() + > regionSlotsCounter.get(index + 1).getLeft()) { + Collections.swap(regionSlotsCounter, index, index + 1); + index += 1; + } } } diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/PartitionInfo.java b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/PartitionInfo.java index 20a701ef34a7..b9af789868a5 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/PartitionInfo.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/PartitionInfo.java @@ -41,6 +41,7 @@ import org.apache.iotdb.confignode.rpc.thrift.TStorageGroupSchema; import org.apache.iotdb.consensus.common.DataSet; import org.apache.iotdb.rpc.TSStatusCode; +import org.apache.iotdb.tsfile.utils.Pair; import org.apache.thrift.TException; import org.apache.thrift.protocol.TBinaryProtocol; @@ -58,10 +59,10 @@ import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.util.ArrayList; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -74,10 +75,13 @@ public class PartitionInfo implements SnapshotProcessor { private static final Logger LOGGER = LoggerFactory.getLogger(PartitionInfo.class); + // Region read write lock private final ReentrantReadWriteLock regionReadWriteLock; private AtomicInteger nextRegionGroupId = new AtomicInteger(0); - private final Map regionMap; + private final Map regionReplicaMap; + // Map + private final Map regionSlotsCounter; // SchemaPartition read write lock private final ReentrantReadWriteLock schemaPartitionReadWriteLock; @@ -94,7 +98,8 @@ public class PartitionInfo implements SnapshotProcessor { public PartitionInfo() { this.regionReadWriteLock = new ReentrantReadWriteLock(); - this.regionMap = new HashMap<>(); + this.regionReplicaMap = new HashMap<>(); + this.regionSlotsCounter = new HashMap<>(); this.schemaPartitionReadWriteLock = new ReentrantReadWriteLock(); this.schemaPartition = @@ -133,7 +138,8 @@ public TSStatus createRegions(CreateRegionsReq req) { int maxRegionId = Integer.MIN_VALUE; for (TRegionReplicaSet regionReplicaSet : req.getRegionMap().values()) { - regionMap.put(regionReplicaSet.getRegionId(), regionReplicaSet); + regionReplicaMap.put(regionReplicaSet.getRegionId(), regionReplicaSet); + regionSlotsCounter.put(regionReplicaSet.getRegionId(), 0L); maxRegionId = Math.max(maxRegionId, regionReplicaSet.getRegionId().getId()); } @@ -161,7 +167,8 @@ public TSStatus deleteRegions(DeleteRegionsReq req) { regionReadWriteLock.writeLock().lock(); try { for (TConsensusGroupId consensusGroupId : req.getConsensusGroupIds()) { - regionMap.remove(consensusGroupId); + regionReplicaMap.remove(consensusGroupId); + regionSlotsCounter.remove(consensusGroupId); } result = new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } finally { @@ -220,6 +227,7 @@ public DataSet getSchemaPartition(GetSchemaPartitionReq req) { */ public TSStatus createSchemaPartition(CreateSchemaPartitionReq req) { schemaPartitionReadWriteLock.writeLock().lock(); + regionReadWriteLock.writeLock().lock(); try { // Allocate SchemaPartition by CreateSchemaPartitionPlan @@ -228,10 +236,14 @@ public TSStatus createSchemaPartition(CreateSchemaPartitionReq req) { assignedResult.forEach( (storageGroup, partitionSlots) -> partitionSlots.forEach( - (seriesPartitionSlot, regionReplicaSet) -> - schemaPartition.createSchemaPartition( - storageGroup, seriesPartitionSlot, regionReplicaSet))); + (seriesPartitionSlot, regionReplicaSet) -> { + schemaPartition.createSchemaPartition( + storageGroup, seriesPartitionSlot, regionReplicaSet); + regionSlotsCounter.computeIfPresent( + regionReplicaSet.getRegionId(), (consensusGroupId, count) -> (count + 1)); + })); } finally { + regionReadWriteLock.writeLock().unlock(); schemaPartitionReadWriteLock.writeLock().unlock(); } @@ -289,6 +301,7 @@ public DataSet getDataPartition(GetDataPartitionReq req) { */ public TSStatus createDataPartition(CreateDataPartitionReq req) { dataPartitionReadWriteLock.writeLock().lock(); + regionReadWriteLock.writeLock().lock(); try { // Allocate DataPartition by CreateDataPartitionPlan @@ -301,13 +314,18 @@ public TSStatus createDataPartition(CreateDataPartitionReq req) { timePartitionSlotRegionReplicaSets.forEach( ((timePartitionSlot, regionReplicaSets) -> regionReplicaSets.forEach( - regionReplicaSet -> - dataPartition.createDataPartition( - storageGroup, - seriesPartitionSlot, - timePartitionSlot, - regionReplicaSet))))))); + regionReplicaSet -> { + dataPartition.createDataPartition( + storageGroup, + seriesPartitionSlot, + timePartitionSlot, + regionReplicaSet); + regionSlotsCounter.computeIfPresent( + regionReplicaSet.getRegionId(), + (consensusGroupId, count) -> (count + 1)); + })))))); } finally { + regionReadWriteLock.writeLock().unlock(); dataPartitionReadWriteLock.writeLock().unlock(); } @@ -341,7 +359,7 @@ public List getRegionReplicaSets(List grou regionReadWriteLock.readLock().lock(); try { for (TConsensusGroupId groupId : groupIds) { - result.add(regionMap.get(groupId)); + result.add(regionReplicaMap.get(groupId)); } } finally { regionReadWriteLock.readLock().unlock(); @@ -354,7 +372,35 @@ public List getAllocatedRegions() { List result; regionReadWriteLock.readLock().lock(); try { - result = new ArrayList<>(regionMap.values()); + result = new ArrayList<>(regionReplicaMap.values()); + } finally { + regionReadWriteLock.readLock().unlock(); + } + return result; + } + + /** @return A copy of regionReplicaMap */ + public Map getRegionReplicaMap() { + Map result; + regionReadWriteLock.readLock().lock(); + try { + result = new HashMap<>(regionReplicaMap); + } finally { + regionReadWriteLock.readLock().unlock(); + } + return result; + } + + /** @return The specific Regions that sorted by the number of allocated slots */ + public List> getSortedRegionSlotsCounter( + List consensusGroupIds) { + List> result = new ArrayList<>(); + regionReadWriteLock.readLock().lock(); + try { + for (TConsensusGroupId consensusGroupId : consensusGroupIds) { + result.add(new Pair<>(regionSlotsCounter.get(consensusGroupId), consensusGroupId)); + } + result.sort(Comparator.comparingLong(Pair::getLeft)); } finally { regionReadWriteLock.readLock().unlock(); } @@ -491,14 +537,22 @@ public SchemaPartition getSchemaPartition() { return schemaPartition; } + @TestOnly + public Map getRegionSlotsCounter() { + return regionSlotsCounter; + } + private void serializeRegionMap(ByteBuffer buffer) throws TException, IOException { try (ByteArrayOutputStream out = new ByteArrayOutputStream(); TIOStreamTransport tioStreamTransport = new TIOStreamTransport(out)) { TProtocol protocol = new TBinaryProtocol(tioStreamTransport); - for (Entry entry : regionMap.entrySet()) { - entry.getKey().write(protocol); - entry.getValue().write(protocol); + + for (TConsensusGroupId consensusGroupId : regionReplicaMap.keySet()) { + consensusGroupId.write(protocol); + regionReplicaMap.get(consensusGroupId).write(protocol); + protocol.writeI64(regionSlotsCounter.get(consensusGroupId)); } + byte[] toArray = out.toByteArray(); buffer.putInt(toArray.length); buffer.put(toArray); @@ -513,18 +567,23 @@ private void deserializeRegionMap(ByteBuffer buffer) throws TException, IOExcept TIOStreamTransport tioStreamTransport = new TIOStreamTransport(in)) { while (in.available() > 0) { TProtocol protocol = new TBinaryProtocol(tioStreamTransport); + TConsensusGroupId tConsensusGroupId = new TConsensusGroupId(); tConsensusGroupId.read(protocol); TRegionReplicaSet tRegionReplicaSet = new TRegionReplicaSet(); tRegionReplicaSet.read(protocol); - regionMap.put(tConsensusGroupId, tRegionReplicaSet); + Long count = protocol.readI64(); + + regionReplicaMap.put(tConsensusGroupId, tRegionReplicaSet); + regionSlotsCounter.put(tConsensusGroupId, count); } } } public void clear() { nextRegionGroupId = new AtomicInteger(0); - regionMap.clear(); + regionReplicaMap.clear(); + regionSlotsCounter.clear(); if (schemaPartition.getSchemaPartitionMap() != null) { schemaPartition.getSchemaPartitionMap().clear(); diff --git a/confignode/src/test/java/org/apache/iotdb/confignode/persistence/PartitionInfoTest.java b/confignode/src/test/java/org/apache/iotdb/confignode/persistence/PartitionInfoTest.java index e6668cd77820..820ec3c1e7cf 100644 --- a/confignode/src/test/java/org/apache/iotdb/confignode/persistence/PartitionInfoTest.java +++ b/confignode/src/test/java/org/apache/iotdb/confignode/persistence/PartitionInfoTest.java @@ -53,7 +53,6 @@ public class PartitionInfoTest { private static final File snapshotDir = new File(BASE_OUTPUT_PATH, "snapshot"); enum testFlag { - RegionReplica(10), DataPartition(20), SchemaPartition(30); @@ -89,25 +88,40 @@ public void testSnapshot() throws TException, IOException { partitionInfo.generateNextRegionGroupId(); + // Create a SchemaRegion CreateRegionsReq createRegionsReq = new CreateRegionsReq(); + TRegionReplicaSet schemaRegionReplicaSet = + generateTRegionReplicaSet( + testFlag.SchemaPartition.getFlag(), + generateTConsensusGroupId( + testFlag.SchemaPartition.getFlag(), TConsensusGroupType.SchemaRegion)); + createRegionsReq.addRegion("root.test", schemaRegionReplicaSet); + partitionInfo.createRegions(createRegionsReq); - TRegionReplicaSet tRegionReplicaSet = + // Create a DataRegion + createRegionsReq = new CreateRegionsReq(); + TRegionReplicaSet dataRegionReplicaSet = generateTRegionReplicaSet( - testFlag.RegionReplica.getFlag(), - generateTConsensusGroupId(testFlag.RegionReplica.getFlag())); - createRegionsReq.addRegion("root.test", tRegionReplicaSet); + testFlag.DataPartition.getFlag(), + generateTConsensusGroupId( + testFlag.DataPartition.getFlag(), TConsensusGroupType.DataRegion)); + createRegionsReq.addRegion("root.test", dataRegionReplicaSet); partitionInfo.createRegions(createRegionsReq); + // Create a SchemaPartition CreateSchemaPartitionReq createSchemaPartitionReq = generateCreateSchemaPartitionReq( testFlag.SchemaPartition.getFlag(), - generateTConsensusGroupId(testFlag.SchemaPartition.getFlag())); + generateTConsensusGroupId( + testFlag.SchemaPartition.getFlag(), TConsensusGroupType.SchemaRegion)); partitionInfo.createSchemaPartition(createSchemaPartitionReq); + // Create a DataPartition CreateDataPartitionReq createDataPartitionReq = generateCreateDataPartitionReq( testFlag.DataPartition.getFlag(), - generateTConsensusGroupId(testFlag.DataPartition.getFlag())); + generateTConsensusGroupId( + testFlag.DataPartition.getFlag(), TConsensusGroupType.DataRegion)); partitionInfo.createDataPartition(createDataPartitionReq); int nextId = partitionInfo.getNextRegionGroupId(); @@ -117,11 +131,23 @@ public void testSnapshot() throws TException, IOException { Assert.assertEquals(nextId, (int) partitionInfo.getNextRegionGroupId()); + // Check SchemaRegion List reloadTRegionReplicaSet = partitionInfo.getRegionReplicaSets( - Collections.singletonList(generateTConsensusGroupId(testFlag.RegionReplica.getFlag()))); + Collections.singletonList( + generateTConsensusGroupId( + testFlag.SchemaPartition.getFlag(), TConsensusGroupType.SchemaRegion))); Assert.assertEquals(1, reloadTRegionReplicaSet.size()); - Assert.assertEquals(tRegionReplicaSet, reloadTRegionReplicaSet.get(0)); + Assert.assertEquals(schemaRegionReplicaSet, reloadTRegionReplicaSet.get(0)); + + // Check DataRegion + reloadTRegionReplicaSet = + partitionInfo.getRegionReplicaSets( + Collections.singletonList( + generateTConsensusGroupId( + testFlag.DataPartition.getFlag(), TConsensusGroupType.DataRegion))); + Assert.assertEquals(1, reloadTRegionReplicaSet.size()); + Assert.assertEquals(dataRegionReplicaSet, reloadTRegionReplicaSet.get(0)); Assert.assertEquals( createDataPartitionReq.getAssignedDataPartition(), @@ -130,6 +156,11 @@ public void testSnapshot() throws TException, IOException { Assert.assertEquals( createSchemaPartitionReq.getAssignedSchemaPartition(), partitionInfo.getSchemaPartition().getSchemaPartitionMap()); + + Assert.assertEquals(2, partitionInfo.getRegionSlotsCounter().size()); + for (Long count : partitionInfo.getRegionSlotsCounter().values()) { + Assert.assertEquals(1, count.intValue()); + } } private TRegionReplicaSet generateTRegionReplicaSet( @@ -187,7 +218,8 @@ private CreateDataPartitionReq generateCreateDataPartitionReq( return createSchemaPartitionReq; } - private TConsensusGroupId generateTConsensusGroupId(int startFlag) { - return new TConsensusGroupId(TConsensusGroupType.PartitionRegion, 111000 + startFlag); + private TConsensusGroupId generateTConsensusGroupId( + int startFlag, TConsensusGroupType consensusGroupType) { + return new TConsensusGroupId(consensusGroupType, 111000 + startFlag); } } From b1734a082a5dcedf07c27ba75d036b42be775a28 Mon Sep 17 00:00:00 2001 From: Alan Choo <43991780+HeimingZ@users.noreply.github.com> Date: Mon, 16 May 2022 22:00:12 +0800 Subject: [PATCH 021/436] [IOTDB-3160] TsFile will be corrupted when flushing memtable appears OOM (#5892) --- .../iotdb/db/integration/IoTDBRestartIT.java | 101 ++++++++++++------ .../engine/storagegroup/TsFileProcessor.java | 54 +++++++--- .../writer/RestorableTsFileIOWriter.java | 37 +------ 3 files changed, 108 insertions(+), 84 deletions(-) diff --git a/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBRestartIT.java b/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBRestartIT.java index 2b940cfe0066..62e59bd64b70 100644 --- a/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBRestartIT.java +++ b/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBRestartIT.java @@ -18,15 +18,20 @@ */ package org.apache.iotdb.db.integration; +import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.conf.IoTDBConfig; import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.engine.StorageEngine; +import org.apache.iotdb.db.engine.memtable.IMemTable; +import org.apache.iotdb.db.engine.storagegroup.TsFileProcessor; import org.apache.iotdb.db.exception.StorageEngineException; import org.apache.iotdb.db.utils.EnvironmentUtils; import org.apache.iotdb.itbase.category.LocalStandaloneTest; import org.apache.iotdb.jdbc.Config; +import org.junit.After; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; import org.junit.experimental.categories.Category; import org.slf4j.Logger; @@ -48,12 +53,19 @@ public class IoTDBRestartIT { private final Logger logger = LoggerFactory.getLogger(IoTDBRestartIT.class); - @Test - public void testRestart() - throws SQLException, ClassNotFoundException, IOException, StorageEngineException { + @Before + public void setUp() throws Exception { EnvironmentUtils.envSetUp(); Class.forName(Config.JDBC_DRIVER_NAME); + } + + @After + public void tearDown() throws Exception { + EnvironmentUtils.cleanEnv(); + } + @Test + public void testRestart() throws SQLException, IOException, StorageEngineException { try (Connection connection = DriverManager.getConnection( Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root", "root"); @@ -99,16 +111,10 @@ public void testRestart() } } } - - EnvironmentUtils.cleanEnv(); } @Test - public void testRestartDelete() - throws SQLException, ClassNotFoundException, IOException, StorageEngineException { - EnvironmentUtils.envSetUp(); - Class.forName(Config.JDBC_DRIVER_NAME); - + public void testRestartDelete() throws SQLException, IOException, StorageEngineException { try (Connection connection = DriverManager.getConnection( Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root", "root"); @@ -169,16 +175,11 @@ public void testRestartDelete() resultSet.close(); } } - - EnvironmentUtils.cleanEnv(); } @Test public void testRestartQueryLargerThanEndTime() throws SQLException, ClassNotFoundException, IOException, StorageEngineException { - EnvironmentUtils.envSetUp(); - Class.forName(Config.JDBC_DRIVER_NAME); - try (Connection connection = DriverManager.getConnection( Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root", "root"); @@ -228,16 +229,11 @@ public void testRestartQueryLargerThanEndTime() } assertEquals(1, cnt); } - - EnvironmentUtils.cleanEnv(); } @Test public void testRestartEndTime() throws SQLException, ClassNotFoundException, IOException, StorageEngineException { - EnvironmentUtils.envSetUp(); - Class.forName(Config.JDBC_DRIVER_NAME); - try (Connection connection = DriverManager.getConnection( Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root", "root"); @@ -284,15 +280,10 @@ public void testRestartEndTime() } assertEquals(2, cnt); } - - EnvironmentUtils.cleanEnv(); } @Test public void testRecoverWALMismatchDataType() throws Exception { - EnvironmentUtils.envSetUp(); - Class.forName(Config.JDBC_DRIVER_NAME); - try (Connection connection = DriverManager.getConnection( Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root", "root"); @@ -324,15 +315,10 @@ public void testRecoverWALMismatchDataType() throws Exception { assertEquals(1, cnt); } } - - EnvironmentUtils.cleanEnv(); } @Test public void testRecoverWALDeleteSchema() throws Exception { - EnvironmentUtils.envSetUp(); - Class.forName(Config.JDBC_DRIVER_NAME); - try (Connection connection = DriverManager.getConnection( Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root", "root"); @@ -361,14 +347,10 @@ public void testRecoverWALDeleteSchema() throws Exception { assertEquals(1, cnt); } } - - EnvironmentUtils.cleanEnv(); } @Test public void testRecoverWALDeleteSchemaCheckResourceTime() throws Exception { - EnvironmentUtils.envSetUp(); - Class.forName(Config.JDBC_DRIVER_NAME); IoTDBConfig config = IoTDBDescriptor.getInstance().getConfig(); int avgSeriesPointNumberThreshold = config.getAvgSeriesPointNumberThreshold(); config.setAvgSeriesPointNumberThreshold(2); @@ -411,6 +393,55 @@ public void testRecoverWALDeleteSchemaCheckResourceTime() throws Exception { config.setAvgSeriesPointNumberThreshold(avgSeriesPointNumberThreshold); config.setSeqTsFileSize(tsFileSize); config.setUnSeqTsFileSize(unFsFileSize); - EnvironmentUtils.cleanEnv(); + } + + @Test + public void testRecoverFromFlushMemTableError() throws Exception { + try (Connection connection = + DriverManager.getConnection( + Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root", "root"); + Statement statement = connection.createStatement()) { + statement.execute("insert into root.turbine1.d1(timestamp,s1,s2) values(1,1.1,2.2)"); + } + + // mock exception + TsFileProcessor[] tsFileProcessors = + StorageEngine.getInstance() + .getProcessorByDataRegionId(new PartialPath("root.turbine1"), 0) + .getWorkSequenceTsFileProcessors() + .toArray(new TsFileProcessor[0]); + Assert.assertEquals(1, tsFileProcessors.length); + IMemTable memTable = tsFileProcessors[0].getWorkMemTable(); + memTable.clear(); + memTable.addTextDataSize(1); + try (Connection connection = + DriverManager.getConnection( + Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root", "root"); + Statement statement = connection.createStatement()) { + statement.execute("flush"); + } + + IoTDBDescriptor.getInstance().getConfig().setReadOnly(false); + EnvironmentUtils.restartDaemon(); + + try (Connection connection = + DriverManager.getConnection( + Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root", "root"); + Statement statement = connection.createStatement()) { + + boolean hasResultSet = statement.execute("select * from root.**"); + assertTrue(hasResultSet); + + try (ResultSet resultSet = statement.getResultSet()) { + int cnt = 0; + while (resultSet.next()) { + Assert.assertEquals("1", resultSet.getString(1)); + Assert.assertEquals("1.1", resultSet.getString(2)); + Assert.assertEquals("2.2", resultSet.getString(3)); + cnt++; + } + Assert.assertEquals(1, cnt); + } + } } } diff --git a/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/TsFileProcessor.java b/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/TsFileProcessor.java index 01fa3116877c..8cac805ba27d 100644 --- a/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/TsFileProcessor.java +++ b/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/TsFileProcessor.java @@ -1191,6 +1191,22 @@ private void releaseFlushedMemTable(IMemTable memTable) { } } + /** This method will synchronize the memTable and release its flushing resources */ + private void syncReleaseFlushedMemTable(IMemTable memTable) { + synchronized (memTable) { + releaseFlushedMemTable(memTable); + memTable.notifyAll(); + if (logger.isDebugEnabled()) { + logger.debug( + "{}: {} released a memtable (signal={}), flushingMemtables size ={}", + storageGroupName, + tsFileResource.getTsFile().getName(), + memTable.isSignalMemTable(), + flushingMemTables.size()); + } + } + } + /** * Take the first MemTable from the flushingMemTables and flush it. Called by a flush thread of * the flush manager pool @@ -1236,7 +1252,29 @@ public void flushOneMemTable() { tsFileResource.getTsFile().getName(), e1); } - Thread.currentThread().interrupt(); + // release resource + try { + syncReleaseFlushedMemTable(memTableToFlush); + // make sure no query will search this file + tsFileResource.setTimeIndex(config.getTimeIndexLevel().getTimeIndex()); + // this callback method will register this empty tsfile into TsFileManager + for (CloseFileListener closeFileListener : closeFileListeners) { + closeFileListener.onClosed(this); + } + // close writer + writer.close(); + writer = null; + synchronized (flushingMemTables) { + flushingMemTables.notifyAll(); + } + } catch (Exception e1) { + logger.error( + "{}: {} Release resource meets error", + storageGroupName, + tsFileResource.getTsFile().getName(), + e1); + } + return; } } } @@ -1278,19 +1316,9 @@ public void flushOneMemTable() { tsFileResource.getTsFile().getName(), memTableToFlush.isSignalMemTable()); } + // for sync flush - synchronized (memTableToFlush) { - releaseFlushedMemTable(memTableToFlush); - memTableToFlush.notifyAll(); - if (logger.isDebugEnabled()) { - logger.debug( - "{}: {} released a memtable (signal={}), flushingMemtables size ={}", - storageGroupName, - tsFileResource.getTsFile().getName(), - memTableToFlush.isSignalMemTable(), - flushingMemTables.size()); - } - } + syncReleaseFlushedMemTable(memTableToFlush); if (shouldClose && flushingMemTables.isEmpty() && writer != null) { try { diff --git a/tsfile/src/main/java/org/apache/iotdb/tsfile/write/writer/RestorableTsFileIOWriter.java b/tsfile/src/main/java/org/apache/iotdb/tsfile/write/writer/RestorableTsFileIOWriter.java index 7cabb9261296..78253124b89a 100644 --- a/tsfile/src/main/java/org/apache/iotdb/tsfile/write/writer/RestorableTsFileIOWriter.java +++ b/tsfile/src/main/java/org/apache/iotdb/tsfile/write/writer/RestorableTsFileIOWriter.java @@ -75,42 +75,7 @@ public class RestorableTsFileIOWriter extends TsFileIOWriter { * @throws IOException if write failed, or the file is broken but autoRepair==false. */ public RestorableTsFileIOWriter(File file) throws IOException { - if (logger.isDebugEnabled()) { - logger.debug("{} is opened.", file.getName()); - } - this.file = file; - this.out = FSFactoryProducer.getFileOutputFactory().getTsFileOutput(file.getPath(), true); - - // file doesn't exist - if (file.length() == 0) { - startFile(); - crashed = true; - canWrite = true; - return; - } - - if (file.exists()) { - try (TsFileSequenceReader reader = new TsFileSequenceReader(file.getAbsolutePath(), false)) { - - truncatedSize = reader.selfCheck(knownSchemas, chunkGroupMetadataList, true); - minPlanIndex = reader.getMinPlanIndex(); - maxPlanIndex = reader.getMaxPlanIndex(); - if (truncatedSize == TsFileCheckStatus.COMPLETE_FILE) { - crashed = false; - canWrite = false; - out.close(); - } else if (truncatedSize == TsFileCheckStatus.INCOMPATIBLE_FILE) { - out.close(); - throw new NotCompatibleTsFileException( - String.format("%s is not in TsFile format.", file.getAbsolutePath())); - } else { - crashed = true; - canWrite = true; - // remove broken data - out.truncate(truncatedSize); - } - } - } + this(file, true); } public RestorableTsFileIOWriter(File file, boolean truncate) throws IOException { From 7d91044ddac135287521d698de3f19b31794789e Mon Sep 17 00:00:00 2001 From: YongzaoDan <33111881+CRZbulabula@users.noreply.github.com> Date: Mon, 16 May 2022 23:26:06 +0800 Subject: [PATCH 022/436] [IOTDB-3181] Region expansion based on the total number of cpu cores (#5919) --- .../conf/iotdb-confignode.properties | 4 +- .../client/AsyncDataNodeClientPool.java | 128 ++++++++- .../iotdb/confignode/conf/ConfigNodeConf.java | 4 +- .../request/write/CreateRegionsReq.java | 32 ++- .../request/write/RegisterDataNodeReq.java | 22 +- ...ationsResp.java => DataNodeInfosResp.java} | 18 +- .../confignode/manager/ConfigManager.java | 4 +- .../iotdb/confignode/manager/NodeManager.java | 40 +-- .../confignode/manager/PartitionManager.java | 7 +- .../confignode/manager/load/LoadManager.java | 267 +++++++----------- .../manager/load/balancer/RegionBalancer.java | 94 +++++- .../allocator/CopySetRegionAllocator.java | 25 +- .../allocator/IRegionAllocator.java | 6 +- .../persistence/ClusterSchemaInfo.java | 27 +- .../confignode/persistence/NodeInfo.java | 79 +++--- .../confignode/persistence/PartitionInfo.java | 10 +- .../impl/DeleteStorageGroupProcedure.java | 13 +- .../thrift/ConfigNodeRPCServiceProcessor.java | 13 +- .../request/ConfigRequestSerDeTest.java | 9 +- .../confignode/persistence/NodeInfoTest.java | 12 +- .../ConfigNodeRPCServiceProcessorTest.java | 37 +-- .../utils/ThriftCommonsSerDeUtils.java | 19 ++ .../iotdb/db/client/ConfigNodeClient.java | 7 +- .../org/apache/iotdb/db/service/DataNode.java | 9 +- thrift-commons/src/main/thrift/common.thrift | 5 +- .../src/main/thrift/confignode.thrift | 11 +- 26 files changed, 552 insertions(+), 350 deletions(-) rename confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/{DataNodeLocationsResp.java => DataNodeInfosResp.java} (70%) rename confignode/src/main/java/org/apache/iotdb/confignode/manager/load/{ => balancer}/allocator/CopySetRegionAllocator.java (84%) rename confignode/src/main/java/org/apache/iotdb/confignode/manager/load/{ => balancer}/allocator/IRegionAllocator.java (90%) diff --git a/confignode/src/assembly/resources/conf/iotdb-confignode.properties b/confignode/src/assembly/resources/conf/iotdb-confignode.properties index 2ca585b2d146..86ec5c1cf82c 100644 --- a/confignode/src/assembly/resources/conf/iotdb-confignode.properties +++ b/confignode/src/assembly/resources/conf/iotdb-confignode.properties @@ -237,8 +237,8 @@ target_confignode=0.0.0.0:22277 # The heartbeat interval in milliseconds, default is 3000ms # Datatype: long -# heartbeat_interval=3000 +# heartbeat_interval=1000 # This parameter only exists for a few days -# enable_heartbeat=false \ No newline at end of file +# enable_heartbeat=true \ No newline at end of file diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/client/AsyncDataNodeClientPool.java b/confignode/src/main/java/org/apache/iotdb/confignode/client/AsyncDataNodeClientPool.java index 3e156986ae22..96d2fe5f1ecf 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/client/AsyncDataNodeClientPool.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/client/AsyncDataNodeClientPool.java @@ -18,12 +18,16 @@ */ package org.apache.iotdb.confignode.client; +import org.apache.iotdb.common.rpc.thrift.TConsensusGroupId; +import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation; import org.apache.iotdb.common.rpc.thrift.TEndPoint; import org.apache.iotdb.common.rpc.thrift.THeartbeatReq; +import org.apache.iotdb.common.rpc.thrift.TRegionReplicaSet; import org.apache.iotdb.commons.client.IClientManager; import org.apache.iotdb.commons.client.async.AsyncDataNodeInternalServiceClient; import org.apache.iotdb.confignode.client.handlers.CreateRegionHandler; import org.apache.iotdb.confignode.client.handlers.HeartbeatHandler; +import org.apache.iotdb.confignode.consensus.request.write.CreateRegionsReq; import org.apache.iotdb.mpp.rpc.thrift.TCreateDataRegionReq; import org.apache.iotdb.mpp.rpc.thrift.TCreateSchemaRegionReq; @@ -32,6 +36,11 @@ import org.slf4j.LoggerFactory; import java.io.IOException; +import java.util.BitSet; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.concurrent.CountDownLatch; /** Asynchronously send RPC requests to DataNodes. See mpp.thrift for more details. */ public class AsyncDataNodeClientPool { @@ -47,12 +56,120 @@ private AsyncDataNodeClientPool() { new ConfigNodeClientPoolFactory.AsyncDataNodeInternalServiceClientPoolFactory()); } + /** + * Execute CreateRegionsReq asynchronously + * + * @param createRegionsReq CreateRegionsReq + * @param ttlMap Map + */ + public void createRegions(CreateRegionsReq createRegionsReq, Map ttlMap) { + + // Index of each Region + int index = 0; + // Number of regions to be created + int regionNum = 0; + // Map> + Map> indexMap = new TreeMap<>(); + + // Assign an independent index to each Region + for (Map.Entry> entry : + createRegionsReq.getRegionMap().entrySet()) { + for (TRegionReplicaSet regionReplicaSet : entry.getValue()) { + regionNum += regionReplicaSet.getDataNodeLocationsSize(); + for (TDataNodeLocation dataNodeLocation : regionReplicaSet.getDataNodeLocations()) { + indexMap + .computeIfAbsent(regionReplicaSet.getRegionId(), idMap -> new TreeMap<>()) + .put(dataNodeLocation.getDataNodeId(), index); + index += 1; + } + } + } + + BitSet bitSet = new BitSet(regionNum); + for (int retry = 0; retry < 3; retry++) { + CountDownLatch latch = new CountDownLatch(regionNum - bitSet.cardinality()); + createRegionsReq + .getRegionMap() + .forEach( + (storageGroup, regionReplicaSets) -> { + // Enumerate each RegionReplicaSet + regionReplicaSets.forEach( + regionReplicaSet -> { + // Enumerate each Region + regionReplicaSet + .getDataNodeLocations() + .forEach( + dataNodeLocation -> { + // Skip those created successfully + if (!bitSet.get( + indexMap + .get(regionReplicaSet.getRegionId()) + .get(dataNodeLocation.getDataNodeId()))) { + TEndPoint endPoint = dataNodeLocation.getInternalEndPoint(); + CreateRegionHandler handler = + new CreateRegionHandler( + indexMap + .get(regionReplicaSet.getRegionId()) + .get(dataNodeLocation.getDataNodeId()), + bitSet, + latch, + regionReplicaSet.getRegionId(), + dataNodeLocation); + + switch (regionReplicaSet.getRegionId().getType()) { + case SchemaRegion: + createSchemaRegion( + endPoint, + genCreateSchemaRegionReq(storageGroup, regionReplicaSet), + handler); + break; + case DataRegion: + createDataRegion( + endPoint, + genCreateDataRegionReq( + storageGroup, + regionReplicaSet, + ttlMap.get(storageGroup)), + handler); + } + } + }); + }); + }); + + try { + // Waiting until this batch of create requests done + latch.await(); + } catch (InterruptedException e) { + LOGGER.error("ClusterSchemaManager was interrupted during create Regions on DataNodes", e); + } + + if (bitSet.cardinality() == regionNum) { + // Break if all creations success + break; + } + } + + if (bitSet.cardinality() < regionNum) { + LOGGER.error( + "Failed to create some SchemaRegions or DataRegions on DataNodes. Please check former logs."); + } + } + + private TCreateSchemaRegionReq genCreateSchemaRegionReq( + String storageGroup, TRegionReplicaSet regionReplicaSet) { + TCreateSchemaRegionReq req = new TCreateSchemaRegionReq(); + req.setStorageGroup(storageGroup); + req.setRegionReplicaSet(regionReplicaSet); + return req; + } + /** * Create a SchemaRegion on specific DataNode * * @param endPoint The specific DataNode */ - public void createSchemaRegion( + private void createSchemaRegion( TEndPoint endPoint, TCreateSchemaRegionReq req, CreateRegionHandler handler) { AsyncDataNodeInternalServiceClient client; try { @@ -65,6 +182,15 @@ public void createSchemaRegion( } } + private TCreateDataRegionReq genCreateDataRegionReq( + String storageGroup, TRegionReplicaSet regionReplicaSet, long TTL) { + TCreateDataRegionReq req = new TCreateDataRegionReq(); + req.setStorageGroup(storageGroup); + req.setRegionReplicaSet(regionReplicaSet); + req.setTtl(TTL); + return req; + } + /** * Create a DataRegion on specific DataNode * diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/conf/ConfigNodeConf.java b/confignode/src/main/java/org/apache/iotdb/confignode/conf/ConfigNodeConf.java index 923fe5c7003c..48e830d3e510 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/conf/ConfigNodeConf.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/conf/ConfigNodeConf.java @@ -129,10 +129,10 @@ public class ConfigNodeConf { Math.max(Runtime.getRuntime().availableProcessors() / 4, 16); /** The heartbeat interval in milliseconds */ - private long heartbeatInterval = 3000; + private long heartbeatInterval = 1000; /** This parameter only exists for a few days */ - private boolean enableHeartbeat = false; + private boolean enableHeartbeat = true; ConfigNodeConf() { // empty constructor diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/CreateRegionsReq.java b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/CreateRegionsReq.java index cc8b142721f0..5b9a900f3192 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/CreateRegionsReq.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/CreateRegionsReq.java @@ -26,6 +26,8 @@ import java.io.IOException; import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.TreeMap; @@ -33,19 +35,21 @@ /** Create regions for specific StorageGroups */ public class CreateRegionsReq extends ConfigRequest { - private final Map regionMap; + private final Map> regionMap; public CreateRegionsReq() { super(ConfigRequestType.CreateRegions); this.regionMap = new TreeMap<>(); } - public Map getRegionMap() { + public Map> getRegionMap() { return regionMap; } public void addRegion(String storageGroup, TRegionReplicaSet regionReplicaSet) { - regionMap.put(storageGroup, regionReplicaSet); + regionMap + .computeIfAbsent(storageGroup, regionReplicaSets -> new ArrayList<>()) + .add(regionReplicaSet); } @Override @@ -54,20 +58,28 @@ protected void serializeImpl(ByteBuffer buffer) { buffer.putInt(regionMap.size()); regionMap.forEach( - (storageGroup, regionReplicaSet) -> { + (storageGroup, regionReplicaSets) -> { BasicStructureSerDeUtil.write(storageGroup, buffer); - ThriftCommonsSerDeUtils.serializeTRegionReplicaSet(regionReplicaSet, buffer); + buffer.putInt(regionReplicaSets.size()); + regionReplicaSets.forEach( + regionReplicaSet -> + ThriftCommonsSerDeUtils.serializeTRegionReplicaSet(regionReplicaSet, buffer)); }); } @Override protected void deserializeImpl(ByteBuffer buffer) throws IOException { - int length = buffer.getInt(); - for (int i = 0; i < length; i++) { + int storageGroupNum = buffer.getInt(); + for (int i = 0; i < storageGroupNum; i++) { String storageGroup = BasicStructureSerDeUtil.readString(buffer); - TRegionReplicaSet regionReplicaSet = - ThriftCommonsSerDeUtils.deserializeTRegionReplicaSet(buffer); - regionMap.put(storageGroup, regionReplicaSet); + regionMap.put(storageGroup, new ArrayList<>()); + + int regionReplicaSetNum = buffer.getInt(); + for (int j = 0; j < regionReplicaSetNum; j++) { + TRegionReplicaSet regionReplicaSet = + ThriftCommonsSerDeUtils.deserializeTRegionReplicaSet(buffer); + regionMap.get(storageGroup).add(regionReplicaSet); + } } } diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/RegisterDataNodeReq.java b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/RegisterDataNodeReq.java index b03fef96a4a5..8800891207ad 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/RegisterDataNodeReq.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/RegisterDataNodeReq.java @@ -18,7 +18,7 @@ */ package org.apache.iotdb.confignode.consensus.request.write; -import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation; +import org.apache.iotdb.common.rpc.thrift.TDataNodeInfo; import org.apache.iotdb.commons.utils.ThriftCommonsSerDeUtils; import org.apache.iotdb.confignode.consensus.request.ConfigRequest; import org.apache.iotdb.confignode.consensus.request.ConfigRequestType; @@ -28,42 +28,42 @@ public class RegisterDataNodeReq extends ConfigRequest { - private TDataNodeLocation location; + private TDataNodeInfo info; public RegisterDataNodeReq() { super(ConfigRequestType.RegisterDataNode); } - public RegisterDataNodeReq(TDataNodeLocation location) { + public RegisterDataNodeReq(TDataNodeInfo info) { this(); - this.location = location; + this.info = info; } - public TDataNodeLocation getLocation() { - return location; + public TDataNodeInfo getInfo() { + return info; } @Override protected void serializeImpl(ByteBuffer buffer) { buffer.putInt(ConfigRequestType.RegisterDataNode.ordinal()); - ThriftCommonsSerDeUtils.serializeTDataNodeLocation(location, buffer); + ThriftCommonsSerDeUtils.serializeTDataNodeInfo(info, buffer); } @Override protected void deserializeImpl(ByteBuffer buffer) { - location = ThriftCommonsSerDeUtils.deserializeTDataNodeLocation(buffer); + info = ThriftCommonsSerDeUtils.deserializeTDataNodeInfo(buffer); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - RegisterDataNodeReq plan = (RegisterDataNodeReq) o; - return location.equals(plan.location); + RegisterDataNodeReq that = (RegisterDataNodeReq) o; + return info.equals(that.info); } @Override public int hashCode() { - return Objects.hash(location); + return Objects.hash(info); } } diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/DataNodeLocationsResp.java b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/DataNodeInfosResp.java similarity index 70% rename from confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/DataNodeLocationsResp.java rename to confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/DataNodeInfosResp.java index 0331e40371a6..7718d4713118 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/DataNodeLocationsResp.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/DataNodeInfosResp.java @@ -18,20 +18,20 @@ */ package org.apache.iotdb.confignode.consensus.response; -import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation; +import org.apache.iotdb.common.rpc.thrift.TDataNodeInfo; import org.apache.iotdb.common.rpc.thrift.TSStatus; -import org.apache.iotdb.confignode.rpc.thrift.TDataNodeLocationResp; +import org.apache.iotdb.confignode.rpc.thrift.TDataNodeInfoResp; import org.apache.iotdb.consensus.common.DataSet; import org.apache.iotdb.rpc.TSStatusCode; import java.util.Map; -public class DataNodeLocationsResp implements DataSet { +public class DataNodeInfosResp implements DataSet { private TSStatus status; - private Map dataNodeLocationMap; + private Map dataNodeInfoMap; - public DataNodeLocationsResp() { + public DataNodeInfosResp() { // empty constructor } @@ -43,14 +43,14 @@ public TSStatus getStatus() { return status; } - public void setDataNodeLocations(Map dataNodeLocationMap) { - this.dataNodeLocationMap = dataNodeLocationMap; + public void setDataNodeInfoMap(Map dataNodeInfoMap) { + this.dataNodeInfoMap = dataNodeInfoMap; } - public void convertToRpcDataNodeLocationResp(TDataNodeLocationResp resp) { + public void convertToRpcDataNodeLocationResp(TDataNodeInfoResp resp) { resp.setStatus(status); if (status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) { - resp.setDataNodeLocationMap(dataNodeLocationMap); + resp.setDataNodeInfoMap(dataNodeInfoMap); } } } diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java index 69561b7fcd19..7324a8a1cb71 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java @@ -43,7 +43,7 @@ import org.apache.iotdb.confignode.consensus.request.write.SetTimePartitionIntervalReq; import org.apache.iotdb.confignode.consensus.response.CountStorageGroupResp; import org.apache.iotdb.confignode.consensus.response.DataNodeConfigurationResp; -import org.apache.iotdb.confignode.consensus.response.DataNodeLocationsResp; +import org.apache.iotdb.confignode.consensus.response.DataNodeInfosResp; import org.apache.iotdb.confignode.consensus.response.DataPartitionResp; import org.apache.iotdb.confignode.consensus.response.PermissionInfoResp; import org.apache.iotdb.confignode.consensus.response.SchemaPartitionResp; @@ -158,7 +158,7 @@ public DataSet getDataNodeInfo(GetDataNodeInfoReq getDataNodeInfoReq) { if (status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) { return nodeManager.getDataNodeInfo(getDataNodeInfoReq); } else { - DataNodeLocationsResp dataSet = new DataNodeLocationsResp(); + DataNodeInfosResp dataSet = new DataNodeInfosResp(); dataSet.setStatus(status); return dataSet; } diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/NodeManager.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/NodeManager.java index ea56c4cf0908..ba046e531ac6 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/NodeManager.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/NodeManager.java @@ -19,6 +19,7 @@ package org.apache.iotdb.confignode.manager; import org.apache.iotdb.common.rpc.thrift.TConfigNodeLocation; +import org.apache.iotdb.common.rpc.thrift.TDataNodeInfo; import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation; import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.commons.consensus.ConsensusGroupId; @@ -28,7 +29,7 @@ import org.apache.iotdb.confignode.consensus.request.write.ApplyConfigNodeReq; import org.apache.iotdb.confignode.consensus.request.write.RegisterDataNodeReq; import org.apache.iotdb.confignode.consensus.response.DataNodeConfigurationResp; -import org.apache.iotdb.confignode.consensus.response.DataNodeLocationsResp; +import org.apache.iotdb.confignode.consensus.response.DataNodeInfosResp; import org.apache.iotdb.confignode.persistence.NodeInfo; import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeRegisterReq; import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeRegisterResp; @@ -83,21 +84,22 @@ private void setGlobalConfig(DataNodeConfigurationResp dataSet) { public DataSet registerDataNode(RegisterDataNodeReq req) { DataNodeConfigurationResp dataSet = new DataNodeConfigurationResp(); - if (nodeInfo.containsValue(req.getLocation())) { + if (nodeInfo.isOnlineDataNode(req.getInfo().getLocation())) { // Reset client - AsyncDataNodeClientPool.getInstance().resetClient(req.getLocation().getInternalEndPoint()); + AsyncDataNodeClientPool.getInstance() + .resetClient(req.getInfo().getLocation().getInternalEndPoint()); TSStatus status = new TSStatus(TSStatusCode.DATANODE_ALREADY_REGISTERED.getStatusCode()); status.setMessage("DataNode already registered."); dataSet.setStatus(status); } else { // Persist DataNodeInfo - req.getLocation().setDataNodeId(nodeInfo.generateNextDataNodeId()); + req.getInfo().getLocation().setDataNodeId(nodeInfo.generateNextDataNodeId()); ConsensusWriteResponse resp = getConsensusManager().write(req); dataSet.setStatus(resp.getStatus()); } - dataSet.setDataNodeId(req.getLocation().getDataNodeId()); + dataSet.setDataNodeId(req.getInfo().getLocation().getDataNodeId()); dataSet.setConfigNodeList(nodeInfo.getOnlineConfigNodes()); setGlobalConfig(dataSet); return dataSet; @@ -110,31 +112,19 @@ public DataSet registerDataNode(RegisterDataNodeReq req) { * @return The specific DataNode's info or all DataNode info if dataNodeId in * QueryDataNodeInfoPlan is -1 */ - public DataNodeLocationsResp getDataNodeInfo(GetDataNodeInfoReq req) { - return (DataNodeLocationsResp) getConsensusManager().read(req).getDataset(); - } - - public int getOnlineDataNodeCount() { - return nodeInfo.getOnlineDataNodeCount(); - } - - /** - * Only leader use this interface. - * - * @return all online DataNodes - */ - public List getOnlineDataNodes() { - return nodeInfo.getOnlineDataNodes(); + public DataNodeInfosResp getDataNodeInfo(GetDataNodeInfoReq req) { + return (DataNodeInfosResp) getConsensusManager().read(req).getDataset(); } /** - * Only leader use this interface. + * Only leader use this interface * - * @param dataNodeId the specific DataNodeId - * @return the specific DataNodeLocation + * @param dataNodeId Specific DataNodeId + * @return All online DataNodes if dataNodeId equals -1. And return the specific DataNode + * otherwise. */ - public TDataNodeLocation getOnlineDataNode(int dataNodeId) { - return nodeInfo.getOnlineDataNode(dataNodeId); + public List getOnlineDataNodes(int dataNodeId) { + return nodeInfo.getOnlineDataNodes(dataNodeId); } /** diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/PartitionManager.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/PartitionManager.java index 5d90ab1551f7..6ce4d6dec9f4 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/PartitionManager.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/PartitionManager.java @@ -33,7 +33,6 @@ import org.apache.iotdb.confignode.consensus.request.read.GetSchemaPartitionReq; import org.apache.iotdb.confignode.consensus.request.write.CreateDataPartitionReq; import org.apache.iotdb.confignode.consensus.request.write.CreateSchemaPartitionReq; -import org.apache.iotdb.confignode.consensus.request.write.DeleteRegionsReq; import org.apache.iotdb.confignode.consensus.response.DataPartitionResp; import org.apache.iotdb.confignode.consensus.response.SchemaPartitionResp; import org.apache.iotdb.confignode.exception.NotEnoughDataNodeException; @@ -290,7 +289,7 @@ private void checkAndAllocateRegionsIfNecessary( storageGroupWithoutRegion.add(storageGroup); } } - getLoadManager().allocateAndCreateRegions(storageGroupWithoutRegion, consensusGroupType); + getLoadManager().initializeRegions(storageGroupWithoutRegion, consensusGroupType); } /** Get all allocated RegionReplicaSets */ @@ -346,8 +345,4 @@ private ClusterSchemaManager getClusterSchemaManager() { private LoadManager getLoadManager() { return configManager.getLoadManager(); } - - public TSStatus deleteRegions(DeleteRegionsReq deleteRegionsReq) { - return getConsensusManager().write(deleteRegionsReq).getStatus(); - } } diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/LoadManager.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/LoadManager.java index cae71a74e84b..55654b14375b 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/LoadManager.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/LoadManager.java @@ -20,13 +20,11 @@ import org.apache.iotdb.common.rpc.thrift.TConsensusGroupId; import org.apache.iotdb.common.rpc.thrift.TConsensusGroupType; -import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation; -import org.apache.iotdb.common.rpc.thrift.TEndPoint; +import org.apache.iotdb.common.rpc.thrift.TDataNodeInfo; import org.apache.iotdb.common.rpc.thrift.THeartbeatReq; import org.apache.iotdb.common.rpc.thrift.TRegionReplicaSet; import org.apache.iotdb.commons.exception.MetadataException; import org.apache.iotdb.confignode.client.AsyncDataNodeClientPool; -import org.apache.iotdb.confignode.client.handlers.CreateRegionHandler; import org.apache.iotdb.confignode.client.handlers.HeartbeatHandler; import org.apache.iotdb.confignode.conf.ConfigNodeDescriptor; import org.apache.iotdb.confignode.consensus.request.write.CreateRegionsReq; @@ -35,22 +33,18 @@ import org.apache.iotdb.confignode.manager.ConsensusManager; import org.apache.iotdb.confignode.manager.Manager; import org.apache.iotdb.confignode.manager.NodeManager; -import org.apache.iotdb.confignode.manager.PartitionManager; -import org.apache.iotdb.confignode.manager.load.allocator.CopySetRegionAllocator; -import org.apache.iotdb.confignode.manager.load.allocator.IRegionAllocator; +import org.apache.iotdb.confignode.manager.load.balancer.RegionBalancer; import org.apache.iotdb.confignode.manager.load.heartbeat.HeartbeatCache; import org.apache.iotdb.confignode.rpc.thrift.TStorageGroupSchema; -import org.apache.iotdb.mpp.rpc.thrift.TCreateDataRegionReq; -import org.apache.iotdb.mpp.rpc.thrift.TCreateSchemaRegionReq; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.BitSet; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.CountDownLatch; +import java.util.TreeMap; import java.util.concurrent.TimeUnit; /** @@ -61,37 +55,41 @@ public class LoadManager implements Runnable { private static final Logger LOGGER = LoggerFactory.getLogger(LoadManager.class); - private final long heartbeatInterval = - ConfigNodeDescriptor.getInstance().getConf().getHeartbeatInterval(); - private final Manager configManager; + private final long heartbeatInterval = + ConfigNodeDescriptor.getInstance().getConf().getHeartbeatInterval(); private final HeartbeatCache heartbeatCache; - private final IRegionAllocator regionAllocator; + private final RegionBalancer regionBalancer; + + private final Map replicaScoreMap; // TODO: Interfaces for active, interrupt and reset LoadBalancer public LoadManager(Manager configManager) { this.configManager = configManager; this.heartbeatCache = new HeartbeatCache(); - this.regionAllocator = new CopySetRegionAllocator(); + + this.regionBalancer = new RegionBalancer(configManager); + + this.replicaScoreMap = new TreeMap<>(); } /** - * Allocate and create one Region on DataNode for each StorageGroup. + * Allocate and create one Region for each StorageGroup. TODO: Use procedure to protect create + * Regions process * * @param storageGroups List * @param consensusGroupType TConsensusGroupType of Region to be allocated */ - public void allocateAndCreateRegions( - List storageGroups, TConsensusGroupType consensusGroupType) + public void initializeRegions(List storageGroups, TConsensusGroupType consensusGroupType) throws NotEnoughDataNodeException { CreateRegionsReq createRegionsReq = null; - // TODO: use procedure to protect create Regions process try { - createRegionsReq = allocateRegions(storageGroups, consensusGroupType); + createRegionsReq = + regionBalancer.genRegionsAllocationPlan(storageGroups, consensusGroupType, 1); createRegionsOnDataNodes(createRegionsReq); } catch (MetadataException e) { LOGGER.error("Meet error when create Regions", e); @@ -100,179 +98,106 @@ public void allocateAndCreateRegions( getConsensusManager().write(createRegionsReq); } - private CreateRegionsReq allocateRegions( - List storageGroups, TConsensusGroupType consensusGroupType) - throws NotEnoughDataNodeException, MetadataException { - CreateRegionsReq createRegionsReq = new CreateRegionsReq(); - - List onlineDataNodes = getNodeManager().getOnlineDataNodes(); - List allocatedRegions = getPartitionManager().getAllocatedRegions(); - - for (String storageGroup : storageGroups) { - TStorageGroupSchema storageGroupSchema = - getClusterSchemaManager().getStorageGroupSchemaByName(storageGroup); - int replicationFactor = - consensusGroupType == TConsensusGroupType.SchemaRegion - ? storageGroupSchema.getSchemaReplicationFactor() - : storageGroupSchema.getDataReplicationFactor(); - - if (onlineDataNodes.size() < replicationFactor) { - throw new NotEnoughDataNodeException(); - } - - TRegionReplicaSet newRegion = - regionAllocator.allocateRegion( - onlineDataNodes, - allocatedRegions, - replicationFactor, - new TConsensusGroupId( - consensusGroupType, getPartitionManager().generateNextRegionGroupId())); - createRegionsReq.addRegion(storageGroup, newRegion); - } - - return createRegionsReq; - } - private void createRegionsOnDataNodes(CreateRegionsReq createRegionsReq) throws MetadataException { - // Index of each Region - int index = 0; - // Number of regions to be created - int regionNum = 0; - Map> indexMap = new HashMap<>(); Map ttlMap = new HashMap<>(); - for (Map.Entry entry : createRegionsReq.getRegionMap().entrySet()) { - regionNum += entry.getValue().getDataNodeLocationsSize(); + for (String storageGroup : createRegionsReq.getRegionMap().keySet()) { ttlMap.put( - entry.getKey(), - getClusterSchemaManager().getStorageGroupSchemaByName(entry.getKey()).getTTL()); - for (TDataNodeLocation dataNodeLocation : entry.getValue().getDataNodeLocations()) { - indexMap - .computeIfAbsent(entry.getKey(), sg -> new HashMap<>()) - .put(dataNodeLocation.getDataNodeId(), index); - index += 1; - } + storageGroup, + getClusterSchemaManager().getStorageGroupSchemaByName(storageGroup).getTTL()); } + AsyncDataNodeClientPool.getInstance().createRegions(createRegionsReq, ttlMap); + } - BitSet bitSet = new BitSet(regionNum); - - for (int retry = 0; retry < 3; retry++) { - CountDownLatch latch = new CountDownLatch(regionNum - bitSet.cardinality()); + private THeartbeatReq genHeartbeatReq() { + return new THeartbeatReq(System.currentTimeMillis()); + } - createRegionsReq - .getRegionMap() - .forEach( - (storageGroup, regionReplicaSet) -> { - // Enumerate each Region - regionReplicaSet - .getDataNodeLocations() - .forEach( - dataNodeLocation -> { - // Skip those created successfully - if (!bitSet.get( - indexMap.get(storageGroup).get(dataNodeLocation.getDataNodeId()))) { - TEndPoint endPoint = dataNodeLocation.getInternalEndPoint(); - CreateRegionHandler handler = - new CreateRegionHandler( - indexMap - .get(storageGroup) - .get(dataNodeLocation.getDataNodeId()), - bitSet, - latch, - regionReplicaSet.getRegionId(), - dataNodeLocation); + private void regionExpansion() { + // Currently, we simply expand the number of regions held by each storage group to + // 50% of the total CPU cores to facilitate performance testing of multiple regions - switch (regionReplicaSet.getRegionId().getType()) { - case SchemaRegion: - AsyncDataNodeClientPool.getInstance() - .createSchemaRegion( - endPoint, - genCreateSchemaRegionReq(storageGroup, regionReplicaSet), - handler); - break; - case DataRegion: - AsyncDataNodeClientPool.getInstance() - .createDataRegion( - endPoint, - genCreateDataRegionReq( - storageGroup, - regionReplicaSet, - ttlMap.get(storageGroup)), - handler); - } - } - }); - }); + int totalCoreNum = 0; + List dataNodeInfos = getNodeManager().getOnlineDataNodes(-1); + for (TDataNodeInfo dataNodeInfo : dataNodeInfos) { + totalCoreNum += dataNodeInfo.getCpuCoreNum(); + } + List storageGroups = getClusterSchemaManager().getStorageGroupNames(); + for (String storageGroup : storageGroups) { try { - latch.await(); - } catch (InterruptedException e) { - LOGGER.error("ClusterSchemaManager was interrupted during create Regions on DataNodes", e); - } + TStorageGroupSchema storageGroupSchema = + getClusterSchemaManager().getStorageGroupSchemaByName(storageGroup); + int totalReplicaNum = + storageGroupSchema.getSchemaReplicationFactor() + * storageGroupSchema.getSchemaRegionGroupIdsSize() + + storageGroupSchema.getDataReplicationFactor() + * storageGroupSchema.getDataRegionGroupIdsSize(); + + if (totalReplicaNum < totalCoreNum * 0.5) { + // Allocate more Regions + CreateRegionsReq createRegionsReq; + if (storageGroupSchema.getSchemaRegionGroupIdsSize() * 5 + > storageGroupSchema.getDataRegionGroupIdsSize()) { + // TODO: Find an optimal SchemaRegion:DataRegion rate + // Currently, we just assume that it's 1:5 + int regionNum = + Math.min( + ((int) (totalCoreNum * 0.5) - totalReplicaNum) + / storageGroupSchema.getDataReplicationFactor(), + storageGroupSchema.getSchemaRegionGroupIdsSize() * 5 + - storageGroupSchema.getDataRegionGroupIdsSize()); + createRegionsReq = + regionBalancer.genRegionsAllocationPlan( + Collections.singletonList(storageGroup), + TConsensusGroupType.DataRegion, + regionNum); + } else { + createRegionsReq = + regionBalancer.genRegionsAllocationPlan( + Collections.singletonList(storageGroup), TConsensusGroupType.SchemaRegion, 1); + } - if (bitSet.cardinality() == regionNum) { - break; + // TODO: use procedure to protect this + createRegionsOnDataNodes(createRegionsReq); + getConsensusManager().write(createRegionsReq); + } + } catch (MetadataException e) { + LOGGER.warn("Meet error when doing regionExpansion", e); + } catch (NotEnoughDataNodeException ignore) { + // The LoadManager will expand Regions automatically after there are enough DataNodes. } } - - if (bitSet.cardinality() < regionNum) { - LOGGER.error( - "Failed to create some SchemaRegions or DataRegions on DataNodes. Please check former logs."); - } } - private TCreateSchemaRegionReq genCreateSchemaRegionReq( - String storageGroup, TRegionReplicaSet regionReplicaSet) { - TCreateSchemaRegionReq req = new TCreateSchemaRegionReq(); - req.setStorageGroup(storageGroup); - req.setRegionReplicaSet(regionReplicaSet); - return req; - } - - private TCreateDataRegionReq genCreateDataRegionReq( - String storageGroup, TRegionReplicaSet regionReplicaSet, long TTL) { - TCreateDataRegionReq req = new TCreateDataRegionReq(); - req.setStorageGroup(storageGroup); - req.setRegionReplicaSet(regionReplicaSet); - req.setTtl(TTL); - return req; - } - - private ConsensusManager getConsensusManager() { - return configManager.getConsensusManager(); - } - - private NodeManager getNodeManager() { - return configManager.getNodeManager(); - } - - private ClusterSchemaManager getClusterSchemaManager() { - return configManager.getClusterSchemaManager(); - } - - private PartitionManager getPartitionManager() { - return configManager.getPartitionManager(); - } - - private THeartbeatReq genHeartbeatReq() { - return new THeartbeatReq(System.currentTimeMillis()); + private void doLoadBalancing() { + regionExpansion(); + // TODO: update replicaScoreMap } @Override public void run() { + int balanceCount = 0; while (true) { try { if (getConsensusManager().isLeader()) { // Ask DataNode for heartbeat in every heartbeat interval - List onlineDataNodes = getNodeManager().getOnlineDataNodes(); - for (TDataNodeLocation dataNodeLocation : onlineDataNodes) { + List onlineDataNodes = getNodeManager().getOnlineDataNodes(-1); + for (TDataNodeInfo dataNodeInfo : onlineDataNodes) { HeartbeatHandler handler = - new HeartbeatHandler(dataNodeLocation.getDataNodeId(), heartbeatCache); + new HeartbeatHandler(dataNodeInfo.getLocation().getDataNodeId(), heartbeatCache); AsyncDataNodeClientPool.getInstance() - .getHeartBeat(dataNodeLocation.getInternalEndPoint(), genHeartbeatReq(), handler); + .getHeartBeat( + dataNodeInfo.getLocation().getInternalEndPoint(), genHeartbeatReq(), handler); } + balanceCount += 1; + // TODO: Adjust load balancing period + if (balanceCount == 10) { + doLoadBalancing(); + balanceCount = 0; + } } else { heartbeatCache.discardAllCache(); } @@ -284,4 +209,16 @@ public void run() { } } } + + private ConsensusManager getConsensusManager() { + return configManager.getConsensusManager(); + } + + private NodeManager getNodeManager() { + return configManager.getNodeManager(); + } + + private ClusterSchemaManager getClusterSchemaManager() { + return configManager.getClusterSchemaManager(); + } } diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/balancer/RegionBalancer.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/balancer/RegionBalancer.java index 2e099883d292..cfc23769da34 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/balancer/RegionBalancer.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/balancer/RegionBalancer.java @@ -18,4 +18,96 @@ */ package org.apache.iotdb.confignode.manager.load.balancer; -public class RegionBalancer {} +import org.apache.iotdb.common.rpc.thrift.TConsensusGroupId; +import org.apache.iotdb.common.rpc.thrift.TConsensusGroupType; +import org.apache.iotdb.common.rpc.thrift.TDataNodeInfo; +import org.apache.iotdb.common.rpc.thrift.TRegionReplicaSet; +import org.apache.iotdb.commons.exception.MetadataException; +import org.apache.iotdb.confignode.consensus.request.write.CreateRegionsReq; +import org.apache.iotdb.confignode.exception.NotEnoughDataNodeException; +import org.apache.iotdb.confignode.manager.ClusterSchemaManager; +import org.apache.iotdb.confignode.manager.Manager; +import org.apache.iotdb.confignode.manager.NodeManager; +import org.apache.iotdb.confignode.manager.PartitionManager; +import org.apache.iotdb.confignode.manager.load.balancer.allocator.CopySetRegionAllocator; +import org.apache.iotdb.confignode.manager.load.balancer.allocator.IRegionAllocator; +import org.apache.iotdb.confignode.rpc.thrift.TStorageGroupSchema; + +import java.util.List; + +/** + * The RegionBalancer provides interfaces to generate optimal Region allocation and migration plans + */ +public class RegionBalancer { + + private final Manager configManager; + private final IRegionAllocator regionAllocator; + + public RegionBalancer(Manager configManager) { + this.configManager = configManager; + // TODO: The RegionAllocator should be configurable + this.regionAllocator = new CopySetRegionAllocator(); + } + + /** + * Generate a Regions allocation plan(CreateRegionsReq) + * + * @param storageGroups List + * @param consensusGroupType TConsensusGroupType of the new Regions + * @param regionNum Number of Regions to be allocated per StorageGroup + * @return CreateRegionsReq + * @throws NotEnoughDataNodeException When the number of DataNodes is not enough for allocation + * @throws MetadataException When some StorageGroups don't exist + */ + public CreateRegionsReq genRegionsAllocationPlan( + List storageGroups, TConsensusGroupType consensusGroupType, int regionNum) + throws NotEnoughDataNodeException, MetadataException { + CreateRegionsReq createRegionsReq = new CreateRegionsReq(); + + List onlineDataNodes = getNodeManager().getOnlineDataNodes(-1); + List allocatedRegions = getPartitionManager().getAllocatedRegions(); + + for (String storageGroup : storageGroups) { + // Get schema + TStorageGroupSchema storageGroupSchema = + getClusterSchemaManager().getStorageGroupSchemaByName(storageGroup); + int replicationFactor = + consensusGroupType == TConsensusGroupType.SchemaRegion + ? storageGroupSchema.getSchemaReplicationFactor() + : storageGroupSchema.getDataReplicationFactor(); + + // Check validity + if (onlineDataNodes.size() < replicationFactor) { + throw new NotEnoughDataNodeException(); + } + + for (int i = 0; i < regionNum; i++) { + // Generate allocation plan + TRegionReplicaSet newRegion = + regionAllocator.allocateRegion( + onlineDataNodes, + allocatedRegions, + replicationFactor, + new TConsensusGroupId( + consensusGroupType, getPartitionManager().generateNextRegionGroupId())); + createRegionsReq.addRegion(storageGroup, newRegion); + + allocatedRegions.add(newRegion); + } + } + + return createRegionsReq; + } + + private NodeManager getNodeManager() { + return configManager.getNodeManager(); + } + + private ClusterSchemaManager getClusterSchemaManager() { + return configManager.getClusterSchemaManager(); + } + + private PartitionManager getPartitionManager() { + return configManager.getPartitionManager(); + } +} diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/allocator/CopySetRegionAllocator.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/balancer/allocator/CopySetRegionAllocator.java similarity index 84% rename from confignode/src/main/java/org/apache/iotdb/confignode/manager/load/allocator/CopySetRegionAllocator.java rename to confignode/src/main/java/org/apache/iotdb/confignode/manager/load/balancer/allocator/CopySetRegionAllocator.java index 7cdc8adf153d..c762b833bd44 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/allocator/CopySetRegionAllocator.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/balancer/allocator/CopySetRegionAllocator.java @@ -16,9 +16,10 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.iotdb.confignode.manager.load.allocator; +package org.apache.iotdb.confignode.manager.load.balancer.allocator; import org.apache.iotdb.common.rpc.thrift.TConsensusGroupId; +import org.apache.iotdb.common.rpc.thrift.TDataNodeInfo; import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation; import org.apache.iotdb.common.rpc.thrift.TRegionReplicaSet; @@ -49,7 +50,7 @@ public CopySetRegionAllocator() { @Override public TRegionReplicaSet allocateRegion( - List onlineDataNodes, + List onlineDataNodes, List allocatedRegions, int replicationFactor, TConsensusGroupId consensusGroupId) { @@ -79,12 +80,15 @@ public TRegionReplicaSet allocateRegion( } private void buildWeightList( - List onlineDataNodes, List allocatedRegions) { + List onlineDataNodes, List allocatedRegions) { + + // TODO: The remaining disk capacity of DataNode can also be calculated into the weightList + int maximumRegionNum = 0; Map countMap = new HashMap<>(); - for (TDataNodeLocation dataNodeLocation : onlineDataNodes) { - maxId = Math.max(maxId, dataNodeLocation.getDataNodeId()); - countMap.put(dataNodeLocation, 0); + for (TDataNodeInfo dataNodeInfo : onlineDataNodes) { + maxId = Math.max(maxId, dataNodeInfo.getLocation().getDataNodeId()); + countMap.put(dataNodeInfo.getLocation(), 0); } for (TRegionReplicaSet regionReplicaSet : allocatedRegions) { for (TDataNodeLocation dataNodeLocation : regionReplicaSet.getDataNodeLocations()) { @@ -103,6 +107,7 @@ private void buildWeightList( } } + /** @return A new CopySet based on weighted random */ private TRegionReplicaSet genWeightedRandomRegion(int replicationFactor) { Set checkSet = new HashSet<>(); TRegionReplicaSet randomRegion = new TRegionReplicaSet(); @@ -124,6 +129,14 @@ private TRegionReplicaSet genWeightedRandomRegion(int replicationFactor) { return randomRegion; } + /** + * Do intersection check. + * + * @param allocatedRegions Allocated CopySets. + * @param newRegion A new CopySet. + * @return True if the intersection size between every allocatedRegions and the newRegion are not + * exceed intersectionSize. + */ private boolean intersectionCheck( List allocatedRegions, TRegionReplicaSet newRegion) { BitSet newBit = new BitSet(maxId + 1); diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/allocator/IRegionAllocator.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/balancer/allocator/IRegionAllocator.java similarity index 90% rename from confignode/src/main/java/org/apache/iotdb/confignode/manager/load/allocator/IRegionAllocator.java rename to confignode/src/main/java/org/apache/iotdb/confignode/manager/load/balancer/allocator/IRegionAllocator.java index 1466fd5d569f..a702d9c2e156 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/allocator/IRegionAllocator.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/balancer/allocator/IRegionAllocator.java @@ -16,10 +16,10 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.iotdb.confignode.manager.load.allocator; +package org.apache.iotdb.confignode.manager.load.balancer.allocator; import org.apache.iotdb.common.rpc.thrift.TConsensusGroupId; -import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation; +import org.apache.iotdb.common.rpc.thrift.TDataNodeInfo; import org.apache.iotdb.common.rpc.thrift.TRegionReplicaSet; import java.util.List; @@ -37,7 +37,7 @@ public interface IRegionAllocator { * @return The optimal TRegionReplicaSet derived by the specific algorithm */ TRegionReplicaSet allocateRegion( - List onlineDataNodes, + List onlineDataNodes, List allocatedRegions, int replicationFactor, TConsensusGroupId consensusGroupId); diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/ClusterSchemaInfo.java b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/ClusterSchemaInfo.java index 9bc8e67d59ef..e9a900169836 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/ClusterSchemaInfo.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/ClusterSchemaInfo.java @@ -160,18 +160,27 @@ public TSStatus createRegions(CreateRegionsReq req) { storageGroupReadWriteLock.writeLock().lock(); try { - for (Map.Entry reqEntry : req.getRegionMap().entrySet()) { + for (Map.Entry> reqEntry : req.getRegionMap().entrySet()) { PartialPath partialPathName = new PartialPath(reqEntry.getKey()); TStorageGroupSchema storageGroupSchema = mTree.getStorageGroupNodeByStorageGroupPath(partialPathName).getStorageGroupSchema(); - switch (reqEntry.getValue().getRegionId().getType()) { - case SchemaRegion: - storageGroupSchema.getSchemaRegionGroupIds().add(reqEntry.getValue().getRegionId()); - break; - case DataRegion: - storageGroupSchema.getDataRegionGroupIds().add(reqEntry.getValue().getRegionId()); - break; - } + reqEntry + .getValue() + .forEach( + regionReplicaSet -> { + switch (regionReplicaSet.getRegionId().getType()) { + case SchemaRegion: + storageGroupSchema + .getSchemaRegionGroupIds() + .add(regionReplicaSet.getRegionId()); + break; + case DataRegion: + storageGroupSchema + .getDataRegionGroupIds() + .add(regionReplicaSet.getRegionId()); + break; + } + }); } result.setCode(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } catch (MetadataException e) { diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/NodeInfo.java b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/NodeInfo.java index 343ec9e66753..02765058cb1b 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/NodeInfo.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/NodeInfo.java @@ -19,6 +19,7 @@ package org.apache.iotdb.confignode.persistence; import org.apache.iotdb.common.rpc.thrift.TConfigNodeLocation; +import org.apache.iotdb.common.rpc.thrift.TDataNodeInfo; import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation; import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.commons.snapshot.SnapshotProcessor; @@ -29,7 +30,7 @@ import org.apache.iotdb.confignode.consensus.request.read.GetDataNodeInfoReq; import org.apache.iotdb.confignode.consensus.request.write.ApplyConfigNodeReq; import org.apache.iotdb.confignode.consensus.request.write.RegisterDataNodeReq; -import org.apache.iotdb.confignode.consensus.response.DataNodeLocationsResp; +import org.apache.iotdb.confignode.consensus.response.DataNodeInfosResp; import org.apache.iotdb.rpc.TSStatusCode; import org.apache.thrift.TException; @@ -79,18 +80,15 @@ public class NodeInfo implements SnapshotProcessor { ConfigNodeDescriptor.getInstance().getConf().getSchemaReplicationFactor(), ConfigNodeDescriptor.getInstance().getConf().getDataReplicationFactor()); - private final ReentrantReadWriteLock configNodeInfoReadWriteLock; - // Online ConfigNodes + private final ReentrantReadWriteLock configNodeInfoReadWriteLock; private final Set onlineConfigNodes; + // Online DataNodes private final ReentrantReadWriteLock dataNodeInfoReadWriteLock; - private AtomicInteger nextDataNodeId = new AtomicInteger(0); - - // Online DataNodes - private final ConcurrentNavigableMap onlineDataNodes = - new ConcurrentSkipListMap(); + private final ConcurrentNavigableMap onlineDataNodes = + new ConcurrentSkipListMap<>(); // For remove or draining DataNode // TODO: implement @@ -105,14 +103,15 @@ public NodeInfo() { new HashSet<>(ConfigNodeDescriptor.getInstance().getConf().getConfigNodeList()); } - public boolean containsValue(TDataNodeLocation info) { + /** @return true if the specific DataNode is now online */ + public boolean isOnlineDataNode(TDataNodeLocation info) { boolean result = false; dataNodeInfoReadWriteLock.readLock().lock(); try { - for (Map.Entry entry : onlineDataNodes.entrySet()) { + for (Map.Entry entry : onlineDataNodes.entrySet()) { info.setDataNodeId(entry.getKey()); - if (entry.getValue().equals(info)) { + if (entry.getValue().getLocation().equals(info)) { result = true; break; } @@ -124,10 +123,6 @@ public boolean containsValue(TDataNodeLocation info) { return result; } - public void put(int dataNodeID, TDataNodeLocation info) { - onlineDataNodes.put(dataNodeID, info); - } - /** * Persist DataNode info * @@ -136,11 +131,12 @@ public void put(int dataNodeID, TDataNodeLocation info) { */ public TSStatus registerDataNode(RegisterDataNodeReq registerDataNodeReq) { TSStatus result; - TDataNodeLocation info = registerDataNodeReq.getLocation(); + TDataNodeInfo info = registerDataNodeReq.getInfo(); dataNodeInfoReadWriteLock.writeLock().lock(); try { - onlineDataNodes.put(info.getDataNodeId(), info); - if (nextDataNodeId.get() < registerDataNodeReq.getLocation().getDataNodeId()) { + onlineDataNodes.put(info.getLocation().getDataNodeId(), info); + + if (nextDataNodeId.get() < info.getLocation().getDataNodeId()) { // In this case, at least one Datanode is registered with the leader node, // so the nextDataNodeID of the followers needs to be added nextDataNodeId.getAndIncrement(); @@ -157,7 +153,7 @@ public TSStatus registerDataNode(RegisterDataNodeReq registerDataNodeReq) { LOGGER.info( "Successfully register DataNode: {}. Current online DataNodes: {}", - info, + info.getLocation(), onlineDataNodes); } finally { dataNodeInfoReadWriteLock.writeLock().unlock(); @@ -172,18 +168,17 @@ public TSStatus registerDataNode(RegisterDataNodeReq registerDataNodeReq) { * @return The specific DataNode's info or all DataNode info if dataNodeId in * QueryDataNodeInfoPlan is -1 */ - public DataNodeLocationsResp getDataNodeInfo(GetDataNodeInfoReq getDataNodeInfoReq) { - DataNodeLocationsResp result = new DataNodeLocationsResp(); + public DataNodeInfosResp getDataNodeInfo(GetDataNodeInfoReq getDataNodeInfoReq) { + DataNodeInfosResp result = new DataNodeInfosResp(); result.setStatus(new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode())); int dataNodeId = getDataNodeInfoReq.getDataNodeID(); dataNodeInfoReadWriteLock.readLock().lock(); try { if (dataNodeId == -1) { - result.setDataNodeLocations(new HashMap<>(onlineDataNodes)); + result.setDataNodeInfoMap(new HashMap<>(onlineDataNodes)); } else { - - result.setDataNodeLocations( + result.setDataNodeInfoMap( Collections.singletonMap(dataNodeId, onlineDataNodes.get(dataNodeId))); } } finally { @@ -204,22 +199,22 @@ public int getOnlineDataNodeCount() { return result; } - public List getOnlineDataNodes() { - List result; - dataNodeInfoReadWriteLock.readLock().lock(); - try { - result = new ArrayList<>(onlineDataNodes.values()); - } finally { - dataNodeInfoReadWriteLock.readLock().unlock(); - } - return result; - } - - public TDataNodeLocation getOnlineDataNode(int dataNodeId) { - TDataNodeLocation result; + /** + * Return the specific online DataNode + * + * @param dataNodeId Specific DataNodeId + * @return All online DataNodes if dataNodeId equals -1. And return the specific DataNode + * otherwise. + */ + public List getOnlineDataNodes(int dataNodeId) { + List result; dataNodeInfoReadWriteLock.readLock().lock(); try { - result = onlineDataNodes.get(dataNodeId); + if (dataNodeId == -1) { + result = new ArrayList<>(onlineDataNodes.values()); + } else { + result = Collections.singletonList(onlineDataNodes.get(dataNodeId)); + } } finally { dataNodeInfoReadWriteLock.readLock().unlock(); } @@ -316,7 +311,7 @@ public boolean processTakeSnapshot(File snapshotDir) throws IOException, TExcept private void serializeOnlineDataNode(DataOutputStream outputStream, TProtocol protocol) throws IOException, TException { outputStream.writeInt(onlineDataNodes.size()); - for (Entry entry : onlineDataNodes.entrySet()) { + for (Entry entry : onlineDataNodes.entrySet()) { outputStream.writeInt(entry.getKey()); entry.getValue().write(protocol); } @@ -368,9 +363,9 @@ private void deserializeOnlineDataNode(DataInputStream inputStream, TProtocol pr int size = inputStream.readInt(); while (size > 0) { int dataNodeId = inputStream.readInt(); - TDataNodeLocation tDataNodeLocation = new TDataNodeLocation(); - tDataNodeLocation.read(protocol); - onlineDataNodes.put(dataNodeId, tDataNodeLocation); + TDataNodeInfo dataNodeInfo = new TDataNodeInfo(); + dataNodeInfo.read(protocol); + onlineDataNodes.put(dataNodeId, dataNodeInfo); size--; } } diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/PartitionInfo.java b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/PartitionInfo.java index b9af789868a5..1f6d4fcdad0f 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/PartitionInfo.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/PartitionInfo.java @@ -137,10 +137,12 @@ public TSStatus createRegions(CreateRegionsReq req) { try { int maxRegionId = Integer.MIN_VALUE; - for (TRegionReplicaSet regionReplicaSet : req.getRegionMap().values()) { - regionReplicaMap.put(regionReplicaSet.getRegionId(), regionReplicaSet); - regionSlotsCounter.put(regionReplicaSet.getRegionId(), 0L); - maxRegionId = Math.max(maxRegionId, regionReplicaSet.getRegionId().getId()); + for (List regionReplicaSets : req.getRegionMap().values()) { + for (TRegionReplicaSet regionReplicaSet : regionReplicaSets) { + regionReplicaMap.put(regionReplicaSet.getRegionId(), regionReplicaSet); + regionSlotsCounter.put(regionReplicaSet.getRegionId(), 0L); + maxRegionId = Math.max(maxRegionId, regionReplicaSet.getRegionId().getId()); + } } if (nextRegionGroupId.get() < maxRegionId) { diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/procedure/impl/DeleteStorageGroupProcedure.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/impl/DeleteStorageGroupProcedure.java index edea9369a68f..35663815000e 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/procedure/impl/DeleteStorageGroupProcedure.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/impl/DeleteStorageGroupProcedure.java @@ -20,7 +20,7 @@ package org.apache.iotdb.confignode.procedure.impl; import org.apache.iotdb.common.rpc.thrift.TConsensusGroupId; -import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation; +import org.apache.iotdb.common.rpc.thrift.TDataNodeInfo; import org.apache.iotdb.common.rpc.thrift.TRegionReplicaSet; import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.commons.exception.runtime.ThriftSerDeException; @@ -193,14 +193,15 @@ private boolean deleteRegion( private void invalidateCache(ConfigNodeProcedureEnv env, String storageGroupName) throws IOException, TException { - List allDataNodes = - env.getConfigManager().getNodeManager().getOnlineDataNodes(); + List allDataNodes = + env.getConfigManager().getNodeManager().getOnlineDataNodes(-1); TInvalidateCacheReq invalidateCacheReq = new TInvalidateCacheReq(); invalidateCacheReq.setStorageGroup(true); invalidateCacheReq.setFullPath(storageGroupName); - for (TDataNodeLocation dataNodeLocation : allDataNodes) { - env.getDataNodeClient(dataNodeLocation).invalidateSchemaCache(invalidateCacheReq); - env.getDataNodeClient(dataNodeLocation).invalidatePartitionCache(invalidateCacheReq); + for (TDataNodeInfo dataNodeInfo : allDataNodes) { + env.getDataNodeClient(dataNodeInfo.getLocation()).invalidateSchemaCache(invalidateCacheReq); + env.getDataNodeClient(dataNodeInfo.getLocation()) + .invalidatePartitionCache(invalidateCacheReq); } } diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessor.java b/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessor.java index 2748f83e657a..c9ec115ad28b 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessor.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessor.java @@ -40,7 +40,7 @@ import org.apache.iotdb.confignode.consensus.request.write.SetTimePartitionIntervalReq; import org.apache.iotdb.confignode.consensus.response.CountStorageGroupResp; import org.apache.iotdb.confignode.consensus.response.DataNodeConfigurationResp; -import org.apache.iotdb.confignode.consensus.response.DataNodeLocationsResp; +import org.apache.iotdb.confignode.consensus.response.DataNodeInfosResp; import org.apache.iotdb.confignode.consensus.response.DataPartitionResp; import org.apache.iotdb.confignode.consensus.response.PermissionInfoResp; import org.apache.iotdb.confignode.consensus.response.SchemaPartitionResp; @@ -54,7 +54,7 @@ import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeRegisterReq; import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeRegisterResp; import org.apache.iotdb.confignode.rpc.thrift.TCountStorageGroupResp; -import org.apache.iotdb.confignode.rpc.thrift.TDataNodeLocationResp; +import org.apache.iotdb.confignode.rpc.thrift.TDataNodeInfoResp; import org.apache.iotdb.confignode.rpc.thrift.TDataNodeRegisterReq; import org.apache.iotdb.confignode.rpc.thrift.TDataNodeRegisterResp; import org.apache.iotdb.confignode.rpc.thrift.TDataPartitionReq; @@ -107,7 +107,7 @@ public ConsensusManager getConsensusManager() { @Override public TDataNodeRegisterResp registerDataNode(TDataNodeRegisterReq req) throws TException { - RegisterDataNodeReq registerReq = new RegisterDataNodeReq(req.getDataNodeLocation()); + RegisterDataNodeReq registerReq = new RegisterDataNodeReq(req.getDataNodeInfo()); DataNodeConfigurationResp registerResp = (DataNodeConfigurationResp) configManager.registerDataNode(registerReq); @@ -121,12 +121,11 @@ public TDataNodeRegisterResp registerDataNode(TDataNodeRegisterReq req) throws T } @Override - public TDataNodeLocationResp getDataNodeLocations(int dataNodeID) throws TException { + public TDataNodeInfoResp getDataNodeInfo(int dataNodeID) throws TException { GetDataNodeInfoReq queryReq = new GetDataNodeInfoReq(dataNodeID); - DataNodeLocationsResp queryResp = - (DataNodeLocationsResp) configManager.getDataNodeInfo(queryReq); + DataNodeInfosResp queryResp = (DataNodeInfosResp) configManager.getDataNodeInfo(queryReq); - TDataNodeLocationResp resp = new TDataNodeLocationResp(); + TDataNodeInfoResp resp = new TDataNodeInfoResp(); queryResp.convertToRpcDataNodeLocationResp(resp); return resp; } diff --git a/confignode/src/test/java/org/apache/iotdb/confignode/consensus/request/ConfigRequestSerDeTest.java b/confignode/src/test/java/org/apache/iotdb/confignode/consensus/request/ConfigRequestSerDeTest.java index eaad26db7bd3..55bad48cf351 100644 --- a/confignode/src/test/java/org/apache/iotdb/confignode/consensus/request/ConfigRequestSerDeTest.java +++ b/confignode/src/test/java/org/apache/iotdb/confignode/consensus/request/ConfigRequestSerDeTest.java @@ -21,6 +21,7 @@ import org.apache.iotdb.common.rpc.thrift.TConfigNodeLocation; import org.apache.iotdb.common.rpc.thrift.TConsensusGroupId; import org.apache.iotdb.common.rpc.thrift.TConsensusGroupType; +import org.apache.iotdb.common.rpc.thrift.TDataNodeInfo; import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation; import org.apache.iotdb.common.rpc.thrift.TEndPoint; import org.apache.iotdb.common.rpc.thrift.TRegionReplicaSet; @@ -86,7 +87,13 @@ public void RegisterDataNodeReqTest() throws IOException { dataNodeLocation.setInternalEndPoint(new TEndPoint("0.0.0.0", 9003)); dataNodeLocation.setDataBlockManagerEndPoint(new TEndPoint("0.0.0.0", 8777)); dataNodeLocation.setConsensusEndPoint(new TEndPoint("0.0.0.0", 7777)); - RegisterDataNodeReq req0 = new RegisterDataNodeReq(dataNodeLocation); + + TDataNodeInfo dataNodeInfo = new TDataNodeInfo(); + dataNodeInfo.setLocation(dataNodeLocation); + dataNodeInfo.setCpuCoreNum(16); + dataNodeInfo.setMaxMemory(34359738368L); + + RegisterDataNodeReq req0 = new RegisterDataNodeReq(dataNodeInfo); req0.serialize(buffer); buffer.flip(); RegisterDataNodeReq req1 = (RegisterDataNodeReq) ConfigRequest.Factory.create(buffer); diff --git a/confignode/src/test/java/org/apache/iotdb/confignode/persistence/NodeInfoTest.java b/confignode/src/test/java/org/apache/iotdb/confignode/persistence/NodeInfoTest.java index febddbe2efa1..4f37010f1fc3 100644 --- a/confignode/src/test/java/org/apache/iotdb/confignode/persistence/NodeInfoTest.java +++ b/confignode/src/test/java/org/apache/iotdb/confignode/persistence/NodeInfoTest.java @@ -18,6 +18,7 @@ */ package org.apache.iotdb.confignode.persistence; +import org.apache.iotdb.common.rpc.thrift.TDataNodeInfo; import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation; import org.apache.iotdb.common.rpc.thrift.TEndPoint; import org.apache.iotdb.confignode.consensus.request.write.RegisterDataNodeReq; @@ -61,10 +62,12 @@ public static void cleanup() throws IOException { @Test public void testSnapshot() throws TException, IOException { - RegisterDataNodeReq registerDataNodeReq = new RegisterDataNodeReq(generateTDataNodeLocation(1)); + RegisterDataNodeReq registerDataNodeReq = + new RegisterDataNodeReq(new TDataNodeInfo(generateTDataNodeLocation(1), 16, 34359738368L)); nodeInfo.registerDataNode(registerDataNodeReq); - registerDataNodeReq = new RegisterDataNodeReq(generateTDataNodeLocation(2)); + registerDataNodeReq = + new RegisterDataNodeReq(new TDataNodeInfo(generateTDataNodeLocation(2), 16, 34359738368L)); nodeInfo.registerDataNode(registerDataNodeReq); Set drainingDataNodes_before = new HashSet<>(); @@ -75,7 +78,7 @@ public void testSnapshot() throws TException, IOException { nodeInfo.setDrainingDataNodes(drainingDataNodes_before); int nextId = nodeInfo.getNextDataNodeId(); - List onlineDataNodes_before = nodeInfo.getOnlineDataNodes(); + List onlineDataNodes_before = nodeInfo.getOnlineDataNodes(-1); nodeInfo.processTakeSnapshot(snapshotDir); nodeInfo.clear(); @@ -86,8 +89,7 @@ public void testSnapshot() throws TException, IOException { Set drainingDataNodes_after = nodeInfo.getDrainingDataNodes(); Assert.assertEquals(drainingDataNodes_before, drainingDataNodes_after); - List onlineDataNodes_after = nodeInfo.getOnlineDataNodes(); - + List onlineDataNodes_after = nodeInfo.getOnlineDataNodes(-1); Assert.assertEquals(onlineDataNodes_before, onlineDataNodes_after); } diff --git a/confignode/src/test/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessorTest.java b/confignode/src/test/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessorTest.java index 2453255d54e6..278c15b6985e 100644 --- a/confignode/src/test/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessorTest.java +++ b/confignode/src/test/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessorTest.java @@ -42,7 +42,7 @@ import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerResp; import org.apache.iotdb.confignode.rpc.thrift.TCheckUserPrivilegesReq; import org.apache.iotdb.confignode.rpc.thrift.TCountStorageGroupResp; -import org.apache.iotdb.confignode.rpc.thrift.TDataNodeLocationResp; +import org.apache.iotdb.confignode.rpc.thrift.TDataNodeInfoResp; import org.apache.iotdb.confignode.rpc.thrift.TDataNodeRegisterReq; import org.apache.iotdb.confignode.rpc.thrift.TDataNodeRegisterResp; import org.apache.iotdb.confignode.rpc.thrift.TDataPartitionReq; @@ -100,7 +100,7 @@ public void before() throws IOException { } @After - public void after() throws IOException, InterruptedException { + public void after() throws IOException { processor.close(); FileUtils.deleteFully(new File(ConfigNodeDescriptor.getInstance().getConf().getConsensusDir())); FileUtils.deleteFully( @@ -133,10 +133,11 @@ private void registerDataNodes() throws TException { dataNodeLocation.setConsensusEndPoint(new TEndPoint("0.0.0.0", 40010 + i)); TDataNodeInfo dataNodeInfo = new TDataNodeInfo(); + dataNodeInfo.setLocation(dataNodeLocation); dataNodeInfo.setCpuCoreNum(8); dataNodeInfo.setMaxMemory(1024 * 1024); - TDataNodeRegisterReq req = new TDataNodeRegisterReq(dataNodeLocation, dataNodeInfo); + TDataNodeRegisterReq req = new TDataNodeRegisterReq(dataNodeInfo); TDataNodeRegisterResp resp = processor.registerDataNode(req); Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), resp.getStatus().getCode()); @@ -157,10 +158,11 @@ public void registerAndQueryDataNodeTest() throws TException { dataNodeLocation.setConsensusEndPoint(new TEndPoint("0.0.0.0", 40011)); TDataNodeInfo dataNodeInfo = new TDataNodeInfo(); + dataNodeInfo.setLocation(dataNodeLocation); dataNodeInfo.setCpuCoreNum(8); dataNodeInfo.setMaxMemory(1024 * 1024); - TDataNodeRegisterReq req = new TDataNodeRegisterReq(dataNodeLocation, dataNodeInfo); + TDataNodeRegisterReq req = new TDataNodeRegisterReq(dataNodeInfo); TDataNodeRegisterResp resp = processor.registerDataNode(req); Assert.assertEquals( TSStatusCode.DATANODE_ALREADY_REGISTERED.getStatusCode(), resp.getStatus().getCode()); @@ -168,35 +170,34 @@ public void registerAndQueryDataNodeTest() throws TException { checkGlobalConfig(resp.getGlobalConfig()); // test query DataNodeInfo - TDataNodeLocationResp locationResp = processor.getDataNodeLocations(-1); + TDataNodeInfoResp infoResp = processor.getDataNodeInfo(-1); Assert.assertEquals( - TSStatusCode.SUCCESS_STATUS.getStatusCode(), locationResp.getStatus().getCode()); - Map locationMap = locationResp.getDataNodeLocationMap(); - Assert.assertEquals(3, locationMap.size()); - List> locationList = - new ArrayList<>(locationMap.entrySet()); - locationList.sort(Comparator.comparingInt(Map.Entry::getKey)); + TSStatusCode.SUCCESS_STATUS.getStatusCode(), infoResp.getStatus().getCode()); + Map infoMap = infoResp.getDataNodeInfoMap(); + Assert.assertEquals(3, infoMap.size()); + List> infoList = new ArrayList<>(infoMap.entrySet()); + infoList.sort(Comparator.comparingInt(Map.Entry::getKey)); for (int i = 0; i < 3; i++) { dataNodeLocation.setDataNodeId(i); dataNodeLocation.setExternalEndPoint(new TEndPoint("0.0.0.0", 6667 + i)); dataNodeLocation.setInternalEndPoint(new TEndPoint("0.0.0.0", 9003 + i)); dataNodeLocation.setDataBlockManagerEndPoint(new TEndPoint("0.0.0.0", 8777 + i)); dataNodeLocation.setConsensusEndPoint(new TEndPoint("0.0.0.0", 40010 + i)); - Assert.assertEquals(dataNodeLocation, locationList.get(i).getValue()); + Assert.assertEquals(dataNodeLocation, infoList.get(i).getValue().getLocation()); } - locationResp = processor.getDataNodeLocations(1); + infoResp = processor.getDataNodeInfo(1); Assert.assertEquals( - TSStatusCode.SUCCESS_STATUS.getStatusCode(), locationResp.getStatus().getCode()); - locationMap = locationResp.getDataNodeLocationMap(); - Assert.assertEquals(1, locationMap.size()); - Assert.assertNotNull(locationMap.get(1)); + TSStatusCode.SUCCESS_STATUS.getStatusCode(), infoResp.getStatus().getCode()); + infoMap = infoResp.getDataNodeInfoMap(); + Assert.assertEquals(1, infoMap.size()); + Assert.assertNotNull(infoMap.get(1)); dataNodeLocation.setDataNodeId(1); dataNodeLocation.setExternalEndPoint(new TEndPoint("0.0.0.0", 6668)); dataNodeLocation.setInternalEndPoint(new TEndPoint("0.0.0.0", 9004)); dataNodeLocation.setDataBlockManagerEndPoint(new TEndPoint("0.0.0.0", 8778)); dataNodeLocation.setConsensusEndPoint(new TEndPoint("0.0.0.0", 40011)); - Assert.assertEquals(dataNodeLocation, locationMap.get(1)); + Assert.assertEquals(dataNodeLocation, infoMap.get(1).getLocation()); } @Test diff --git a/node-commons/src/main/java/org/apache/iotdb/commons/utils/ThriftCommonsSerDeUtils.java b/node-commons/src/main/java/org/apache/iotdb/commons/utils/ThriftCommonsSerDeUtils.java index e641081c06d0..e689d09c952f 100644 --- a/node-commons/src/main/java/org/apache/iotdb/commons/utils/ThriftCommonsSerDeUtils.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/utils/ThriftCommonsSerDeUtils.java @@ -19,6 +19,7 @@ package org.apache.iotdb.commons.utils; import org.apache.iotdb.common.rpc.thrift.TConsensusGroupId; +import org.apache.iotdb.common.rpc.thrift.TDataNodeInfo; import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation; import org.apache.iotdb.common.rpc.thrift.TEndPoint; import org.apache.iotdb.common.rpc.thrift.TRegionReplicaSet; @@ -90,6 +91,24 @@ public static TDataNodeLocation deserializeTDataNodeLocation(ByteBuffer buffer) return dataNodeLocation; } + public static void serializeTDataNodeInfo(TDataNodeInfo dataNodeInfo, ByteBuffer buffer) { + try { + dataNodeInfo.write(generateWriteProtocol(buffer)); + } catch (TException e) { + throw new ThriftSerDeException("Write TDataNodeInfo failed: ", e); + } + } + + public static TDataNodeInfo deserializeTDataNodeInfo(ByteBuffer buffer) { + TDataNodeInfo dataNodeInfo = new TDataNodeInfo(); + try { + dataNodeInfo.read(generateReadProtocol(buffer)); + } catch (TException e) { + throw new ThriftSerDeException("Read TDataNodeInfo failed: ", e); + } + return dataNodeInfo; + } + public static void serializeTSeriesPartitionSlot( TSeriesPartitionSlot seriesPartitionSlot, ByteBuffer buffer) { try { diff --git a/server/src/main/java/org/apache/iotdb/db/client/ConfigNodeClient.java b/server/src/main/java/org/apache/iotdb/db/client/ConfigNodeClient.java index ecff7dbcd35e..a5d34a576f82 100644 --- a/server/src/main/java/org/apache/iotdb/db/client/ConfigNodeClient.java +++ b/server/src/main/java/org/apache/iotdb/db/client/ConfigNodeClient.java @@ -27,7 +27,7 @@ import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerResp; import org.apache.iotdb.confignode.rpc.thrift.TCheckUserPrivilegesReq; import org.apache.iotdb.confignode.rpc.thrift.TCountStorageGroupResp; -import org.apache.iotdb.confignode.rpc.thrift.TDataNodeLocationResp; +import org.apache.iotdb.confignode.rpc.thrift.TDataNodeInfoResp; import org.apache.iotdb.confignode.rpc.thrift.TDataNodeRegisterReq; import org.apache.iotdb.confignode.rpc.thrift.TDataNodeRegisterResp; import org.apache.iotdb.confignode.rpc.thrift.TDataPartitionReq; @@ -186,11 +186,10 @@ public TDataNodeRegisterResp registerDataNode(TDataNodeRegisterReq req) throw new IoTDBConnectionException(MSG_RECONNECTION_FAIL); } - public TDataNodeLocationResp getDataNodeLocations(int dataNodeID) - throws IoTDBConnectionException { + public TDataNodeInfoResp getDataNodeInfos(int dataNodeID) throws IoTDBConnectionException { for (int i = 0; i < RETRY_NUM; i++) { try { - TDataNodeLocationResp resp = client.getDataNodeLocations(dataNodeID); + TDataNodeInfoResp resp = client.getDataNodeInfo(dataNodeID); if (!updateConfigNodeLeader(resp.status)) { return resp; } diff --git a/server/src/main/java/org/apache/iotdb/db/service/DataNode.java b/server/src/main/java/org/apache/iotdb/db/service/DataNode.java index bf77e28b2d5f..0edbf85f6fc1 100644 --- a/server/src/main/java/org/apache/iotdb/db/service/DataNode.java +++ b/server/src/main/java/org/apache/iotdb/db/service/DataNode.java @@ -143,7 +143,8 @@ public void joinCluster() throws StartupException { try { ConfigNodeClient configNodeClient = new ConfigNodeClient(); IoTDBConfig config = IoTDBDescriptor.getInstance().getConfig(); - TDataNodeRegisterReq req = new TDataNodeRegisterReq(); + + // Set DataNodeLocation TDataNodeLocation location = new TDataNodeLocation(); location.setDataNodeId(config.getDataNodeId()); location.setExternalEndPoint(new TEndPoint(config.getRpcAddress(), config.getRpcPort())); @@ -153,13 +154,15 @@ public void joinCluster() throws StartupException { new TEndPoint(config.getInternalIp(), config.getDataBlockManagerPort())); location.setConsensusEndPoint( new TEndPoint(config.getInternalIp(), config.getConsensusPort())); - req.setDataNodeLocation(location); + // Set DataNodeInfo TDataNodeInfo info = new TDataNodeInfo(); + info.setLocation(location); info.setCpuCoreNum(Runtime.getRuntime().availableProcessors()); info.setMaxMemory(Runtime.getRuntime().totalMemory()); - req.setDataNodeInfo(info); + TDataNodeRegisterReq req = new TDataNodeRegisterReq(); + req.setDataNodeInfo(info); TDataNodeRegisterResp dataNodeRegisterResp = configNodeClient.registerDataNode(req); // store config node lists from resp diff --git a/thrift-commons/src/main/thrift/common.thrift b/thrift-commons/src/main/thrift/common.thrift index 5d3b70e9d536..9411682d1fbe 100644 --- a/thrift-commons/src/main/thrift/common.thrift +++ b/thrift-commons/src/main/thrift/common.thrift @@ -83,6 +83,7 @@ struct THeartbeatResp { } struct TDataNodeInfo { - 1: required i32 cpuCoreNum - 2: required i64 maxMemory + 1: required TDataNodeLocation location + 2: required i32 cpuCoreNum + 3: required i64 maxMemory } \ No newline at end of file diff --git a/thrift-confignode/src/main/thrift/confignode.thrift b/thrift-confignode/src/main/thrift/confignode.thrift index 29b4a0444905..f102e2aa3a52 100644 --- a/thrift-confignode/src/main/thrift/confignode.thrift +++ b/thrift-confignode/src/main/thrift/confignode.thrift @@ -23,11 +23,10 @@ namespace py iotdb.thrift.confignode // DataNode struct TDataNodeRegisterReq { - 1: required common.TDataNodeLocation dataNodeLocation - 2: required common.TDataNodeInfo dataNodeInfo + 1: required common.TDataNodeInfo dataNodeInfo // Map // DataNode can use statusMap to report its status to the ConfigNode when restart - 3: optional map statusMap + 2: optional map statusMap } struct TGlobalConfig { @@ -44,10 +43,10 @@ struct TDataNodeRegisterResp { 4: optional TGlobalConfig globalConfig } -struct TDataNodeLocationResp { +struct TDataNodeInfoResp { 1: required common.TSStatus status // map - 2: optional map dataNodeLocationMap + 2: optional map dataNodeInfoMap } // StorageGroup @@ -181,7 +180,7 @@ service ConfigIService { TDataNodeRegisterResp registerDataNode(TDataNodeRegisterReq req) - TDataNodeLocationResp getDataNodeLocations(i32 dataNodeId) + TDataNodeInfoResp getDataNodeInfo(i32 dataNodeId) /* StorageGroup */ From dbee2b89bedea6a9e7610bf92089d779e3a75e60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=B2=9B=E8=BE=B0?= <45144903+choubenson@users.noreply.github.com> Date: Mon, 16 May 2022 23:30:52 +0800 Subject: [PATCH 023/436] [IOTDB-2978][compaction error log ] Log level and "null" error message handling (#5917) --- .../cross/CrossSpaceCompactionTask.java | 9 +++++++-- .../inner/InnerSpaceCompactionTask.java | 16 +++++++--------- .../impl/ReadPointCompactionPerformer.java | 7 +++---- .../compaction/task/AbstractCompactionTask.java | 7 ++++--- 4 files changed, 21 insertions(+), 18 deletions(-) diff --git a/server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/CrossSpaceCompactionTask.java b/server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/CrossSpaceCompactionTask.java index 155c9795fa80..58077be48019 100644 --- a/server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/CrossSpaceCompactionTask.java +++ b/server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/CrossSpaceCompactionTask.java @@ -159,8 +159,13 @@ protected void doCompaction() throws Exception { ((double) selectedFileSize) / 1024.0d / 1024.0d / costTime); } } catch (Throwable throwable) { - // catch throwable instead of exception to handle OOM errors - LOGGER.error("Meet errors in cross space compaction."); + // catch throwable to handle OOM errors + if (!(throwable instanceof InterruptedException)) { + LOGGER.error( + "{} [Compaction] Meet errors in cross space compaction.", fullStorageGroupName); + } + + // handle exception CompactionExceptionHandler.handleException( fullStorageGroupName, logFile, diff --git a/server/src/main/java/org/apache/iotdb/db/engine/compaction/inner/InnerSpaceCompactionTask.java b/server/src/main/java/org/apache/iotdb/db/engine/compaction/inner/InnerSpaceCompactionTask.java index 649468660b72..8f9b538816cc 100644 --- a/server/src/main/java/org/apache/iotdb/db/engine/compaction/inner/InnerSpaceCompactionTask.java +++ b/server/src/main/java/org/apache/iotdb/db/engine/compaction/inner/InnerSpaceCompactionTask.java @@ -113,9 +113,7 @@ protected void doCompaction() throws Exception { + File.separator + targetTsFileResource.getTsFile().getName() + CompactionLogger.INNER_COMPACTION_LOG_NAME_SUFFIX); - CompactionLogger compactionLogger = null; - try { - compactionLogger = new CompactionLogger(logFile); + try (CompactionLogger compactionLogger = new CompactionLogger(logFile)) { compactionLogger.logFiles(selectedTsFileResourceList, CompactionLogger.STR_SOURCE_FILES); compactionLogger.logFiles(targetTsFileList, CompactionLogger.STR_TARGET_FILES); LOGGER.info("{} [InnerSpaceCompactionTask] Close the logger", fullStorageGroupName); @@ -200,13 +198,13 @@ protected void doCompaction() throws Exception { FileUtils.delete(logFile); } } catch (Throwable throwable) { - LOGGER.warn("{} [Compaction] Start to handle exception", fullStorageGroupName); - if (throwable instanceof InterruptedException) { - Thread.currentThread().interrupt(); - } - if (compactionLogger != null) { - compactionLogger.close(); + // catch throwable to handle OOM errors + if (!(throwable instanceof InterruptedException)) { + LOGGER.error( + "{} [Compaction] Meet errors in inner space compaction.", fullStorageGroupName); } + + // handle exception if (isSequence()) { CompactionExceptionHandler.handleException( fullStorageGroupName, diff --git a/server/src/main/java/org/apache/iotdb/db/engine/compaction/performer/impl/ReadPointCompactionPerformer.java b/server/src/main/java/org/apache/iotdb/db/engine/compaction/performer/impl/ReadPointCompactionPerformer.java index 4b7d21562213..3dc0c5ab11d4 100644 --- a/server/src/main/java/org/apache/iotdb/db/engine/compaction/performer/impl/ReadPointCompactionPerformer.java +++ b/server/src/main/java/org/apache/iotdb/db/engine/compaction/performer/impl/ReadPointCompactionPerformer.java @@ -228,10 +228,9 @@ private void compactNonAlignedSeries( for (int i = 0; i < subTaskNums; i++) { try { futures.get(i).get(); - } catch (InterruptedException | ExecutionException e) { - LOGGER.error("SubCompactionTask meet errors ", e); - Thread.currentThread().interrupt(); - throw new InterruptedException(); + } catch (ExecutionException e) { + LOGGER.error("[Compaction] SubCompactionTask meet errors ", e); + throw new IOException(e); } } diff --git a/server/src/main/java/org/apache/iotdb/db/engine/compaction/task/AbstractCompactionTask.java b/server/src/main/java/org/apache/iotdb/db/engine/compaction/task/AbstractCompactionTask.java index 0fbe2e7d40af..c384671619aa 100644 --- a/server/src/main/java/org/apache/iotdb/db/engine/compaction/task/AbstractCompactionTask.java +++ b/server/src/main/java/org/apache/iotdb/db/engine/compaction/task/AbstractCompactionTask.java @@ -74,9 +74,10 @@ public CompactionTaskSummary call() throws Exception { doCompaction(); isSuccess = true; } catch (InterruptedException e) { - LOGGER.warn("Current task is interrupted"); - } catch (Exception e) { - LOGGER.error("Running compaction task failed", e); + LOGGER.warn("{} [Compaction] Current task is interrupted", fullStorageGroupName); + } catch (Throwable e) { + // Use throwable to catch OOM exception. + LOGGER.error("{} [Compaction] Running compaction task failed", fullStorageGroupName, e); } finally { this.currentTaskNum.decrementAndGet(); CompactionTaskManager.getInstance().removeRunningTaskFuture(this); From 719a77ac5e1794c653479ee1dde72aa57c267007 Mon Sep 17 00:00:00 2001 From: ljn55966005 <32378956+ljn55966005@users.noreply.github.com> Date: Tue, 17 May 2022 00:20:40 +0800 Subject: [PATCH 024/436] [IOTDB-2963] metrics of process and hardware (#5406) Co-authored-by: Erickin <956968575@qq.com> Co-authored-by: zhengqiang-cisdi --- .../Maintenance-Tools/Metric-Tool.md | 41 ++++- .../Maintenance-Tools/Metric-Tool.md | 42 ++++- .../iotdb/metrics/DoNothingMetricService.java | 10 ++ .../apache/iotdb/metrics/MetricService.java | 13 ++ .../iotdb/db/service/metrics/Metric.java | 20 ++- .../db/service/metrics/MetricsService.java | 19 +++ .../metrics/ProcessMetricsMonitor.java | 148 ++++++++++++++++++ .../service/metrics/SysRunMetricsMonitor.java | 134 ++++++++++++++++ 8 files changed, 417 insertions(+), 10 deletions(-) create mode 100644 server/src/main/java/org/apache/iotdb/db/service/metrics/ProcessMetricsMonitor.java create mode 100644 server/src/main/java/org/apache/iotdb/db/service/metrics/SysRunMetricsMonitor.java diff --git a/docs/UserGuide/Maintenance-Tools/Metric-Tool.md b/docs/UserGuide/Maintenance-Tools/Metric-Tool.md index 8d3360880241..35b44de4b656 100644 --- a/docs/UserGuide/Maintenance-Tools/Metric-Tool.md +++ b/docs/UserGuide/Maintenance-Tools/Metric-Tool.md @@ -111,25 +111,58 @@ Next, we will choose Prometheus format data as samples to describe each kind of | cost_task_seconds_sum | name="compaction" | important | The total cost seconds of all compaction tasks till now | cost_task_seconds_sum{name="compaction",} 0.363 | | data_written | name="compaction",
type="aligned/not-aligned/total" | important | The size of data written in compaction | data_written{name="compaction",type="total",} 10240 | | data_read | name="compaction" | important | The size of data read in compaction | data_read={name="compaction",} 10240 | -#### 4.3.5. Memory Usage +#### 4.3.5. CPU + +| Metric | Tag | level | 说明 | 示例 | +| ---------------- | ---------- | ----- | ------------------------------------ | -------------------------------------------- | +| process_cpu_load | name="cpu" | core | current process CPU Usage (%) | process_cpu_load{name="process",} 5.0 | +| process_cpu_time | name="cpu" | core | total Process CPU Time Occupied (ns) | process_cpu_time{name="process",} 3.265625E9 | +| sys_cpu_load | name="cpu" | core | current system CPU Usage(%) | sys_cpu_load{name="system",} 15.0 | +| sys_cpu_cores | name="cpu" | core | available CPU cores | sys_cpu_cores{name="system",} 16.0 | + +#### 4.3.6. Memory Usage | Metric | Tag | level | Description | Sample | | ------ | --------------------------------------- | ------ | --------------------------------------------------------------------- | --------------------------------- | | mem | name="chunkMetaData/storageGroup/mtree" | important | Current memory size of chunkMetaData/storageGroup/mtree data in bytes | mem{name="chunkMetaData",} 2050.0 | +| process_max_mem | name="memory" | core | The maximum available memory for the JVM | process_max_mem{name="process",} 3.545759744E9 | +| process_used_mem | name="memory" | core | The current available memory for the JVM | process_used_mem{name="process",} 4.6065456E7 | +| process_total_mem | name="memory" | core | The current requested memory for the JVM | process_total_mem{name="process",} 2.39599616E8 | +| process_free_mem | name="memory" | core | The free available memory for the JVM | process_free_mem{name="process",} 1.94035584E8 | +| process_mem_ratio | name="memory" | core | Memory footprint ratio of process | process_mem_ratio{name="process",} 0.0 | +| sys_total_physical_memory_size | name="memory" | core | Maximum physical memory of system | sys_total_physical_memory_size{name="system",} 1.5950999552E10 | +| sys_free_physical_memory_size | name="memory" | core | The current available memory of system | sys_free_physical_memory_size{name="system",} 4.532396032E9 | +| sys_total_swap_space_size | name="memory" | core | The maximum swap area of system | sys_total_swap_space_size{name="system",} 2.1051273216E10 | +| sys_free_swap_space_size | name="memory" | core | The available swap area of system | sys_free_swap_space_size{name="system",} 2.931576832E9 | +| sys_committed_vm_size | name="memory" | important | the amount of virtual memory available to running processes | sys_committed_vm_size{name="system",} 5.04344576E8 | + +#### 4.3.7 Process Status + +| Metric | Tag | level | 说明 | 示例 | +| --------------------- | -------------- | ----- | ------------------------------------------------------------ | ------------------------------------------- | +| process_threads_count | name="process" | core | The current number of threads | process_threads_count{name="process",} 11.0 | +| process_status | name="process" | core | The process survivor status, 1.0 means survivorship, and 0.0 means terminated | process_status{name="process",} 1.0 | + +#### 4.3.8. 磁盘 + +| Metric | Tag | level | 说明 | 示例 | +| -------------------- | ----------- | ----- | ------------------------- | ----------------------------------------------------- | +| sys_disk_total_space | name="disk" | core | The total disk space | sys_disk_total_space{name="system",} 5.10770798592E11 | +| sys_disk_free_space | name="disk" | core | The available disk space | sys_disk_free_space{name="system",} 3.63467845632E11 | -#### 4.3.6. Cache Hit Ratio +#### 4.3.9. Cache Hit Ratio | Metric | Tag | level | Description | Sample | | --------- | --------------------------------------- | ------ | ----------------------------------------------------------------------------- | --------------------------- | | cache_hit | name="chunk/timeSeriesMeta/bloomFilter" | important | Cache hit ratio of chunk/timeSeriesMeta and prevention ratio of bloom filter | cache_hit{name="chunk",} 80 | -#### 4.3.7. Business Data +#### 4.3.19. Business Data | Metric | Tag | level | Description | Sample | | -------- | ------------------------------------- | ------ | ------------------------------------------------------------- | -------------------------------- | | quantity | name="timeSeries/storageGroup/device" | important | The current count of timeSeries/storageGroup/devices in IoTDB | quantity{name="timeSeries",} 1.0 | -#### 4.3.8. Cluster +#### 4.3.11. Cluster | Metric | Tag | level | Description | Sample | | ------------------------- | ------------------------------- | ------ | -------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | diff --git a/docs/zh/UserGuide/Maintenance-Tools/Metric-Tool.md b/docs/zh/UserGuide/Maintenance-Tools/Metric-Tool.md index 1e989a113a07..d627867c612d 100644 --- a/docs/zh/UserGuide/Maintenance-Tools/Metric-Tool.md +++ b/docs/zh/UserGuide/Maintenance-Tools/Metric-Tool.md @@ -110,25 +110,57 @@ IoTDB对外提供JMX和Prometheus格式的监控指标,对于JMX,可以通 | data_written | name="compaction",
type="aligned/not-aligned/total" | important | 合并文件时写入量 | data_written{name="compaction",type="total",} 10240 | | data_read | name="compaction" | important | 合并文件时的读取量 | data_read={name="compaction",} 10240 | -#### 4.3.5. 内存占用 +#### 4.3.5. CPU +| Metric | Tag | level | 说明 | 示例 | +| ------ | --------------------------------------- | ------ | -------------------------------------------------- | --------------------------------- | +| process_cpu_load | name="cpu" | core | process当前CPU占用率(%) | process_cpu_load{name="process",} 5.0 | +| process_cpu_time | name="cpu" | core | process累计占用CPU时间(ns) | process_cpu_time{name="process",} 3.265625E9 | +| sys_cpu_load | name="cpu" | core | system当前CPU占用率(%) | sys_cpu_load{name="system",} 15.0 | +| sys_cpu_cores | name="cpu" | core | jvm可用处理器数 | sys_cpu_cores{name="system",} 16.0 | + +#### 4.3.6. 内存占用 -| Metric | Tag | 说明 | level | 示例 | +| Metric | Tag | level | 说明 | 示例 | | ------ | --------------------------------------- | ------ | -------------------------------------------------- | --------------------------------- | | mem | name="chunkMetaData/storageGroup/mtree" | important | chunkMetaData/storageGroup/mtree占用的内存(byte) | mem{name="chunkMetaData",} 2050.0 | +| process_max_mem | name="memory" | core | JVM最大可用内存 | process_max_mem{name="process",} 3.545759744E9 | +| process_used_mem | name="memory" | core | JVM当前使用内存 | process_used_mem{name="process",} 4.6065456E7 | +| process_total_mem | name="memory" | core | JVM当前已申请内存 | process_total_mem{name="process",} 2.39599616E8 | +| process_free_mem | name="memory" | core | JVM当前剩余可用内存 | process_free_mem{name="process",} 1.94035584E8 | +| process_mem_ratio | name="memory" | core | 进程的内存占用比例 | process_mem_ratio{name="process",} 0.0 | +| sys_total_physical_memory_size | name="memory" | core | system最大物理内存 | sys_total_physical_memory_size{name="system",} 1.5950999552E10 | +| sys_free_physical_memory_size | name="memory" | core | system当前剩余可用内存 | sys_free_physical_memory_size{name="system",} 4.532396032E9 | +| sys_total_swap_space_size | name="memory" | core | system交换区最大空间 | sys_total_swap_space_size{name="system",} 2.1051273216E10 | +| sys_free_swap_space_size | name="memory" | core | system交换区剩余可用空间 | sys_free_swap_space_size{name="system",} 2.931576832E9 | +| sys_committed_vm_size | name="memory" | important | system保证可用于正在运行的进程的虚拟内存量 | sys_committed_vm_size{name="system",} 5.04344576E8 | + +#### 4.3.7. 进程状态 + +| Metric | Tag | level | 说明 | 示例 | +| --------------------- | -------------- | ----- | ---------------------------------- | ------------------------------------------- | +| process_threads_count | name="process" | core | 当前线程数 | process_threads_count{name="process",} 11.0 | +| process_status | name="process" | core | 进程存活状态,1.0为存活,0.0为终止 | process_status{name="process",} 1.0 | + +#### 4.3.8. 磁盘 + +| Metric | Tag | level | 说明 | 示例 | +| -------------------- | ----------- | ----- | ------------ | ----------------------------------------------------- | +| sys_disk_total_space | name="disk" | core | 磁盘总大小 | sys_disk_total_space{name="system",} 5.10770798592E11 | +| sys_disk_free_space | name="disk" | core | 磁盘可用大小 | sys_disk_free_space{name="system",} 3.63467845632E11 | -#### 4.3.6. 缓存命中率 +#### 4.3.9. 缓存命中率 | Metric | Tag | level | 说明 | 示例 | | --------- | --------------------------------------- | ------ | ------------------------------------------------ | --------------------------- | | cache_hit | name="chunk/timeSeriesMeta/bloomFilter" | important | chunk/timeSeriesMeta缓存命中率,bloomFilter拦截率 | cache_hit{name="chunk",} 80 | -#### 4.3.7. 业务数据 +#### 4.3.10. 业务数据 | Metric | Tag | level | 说明 | 示例 | | -------- | ------------------------------------- | ------ | -------------------------------------------- | -------------------------------- | | quantity | name="timeSeries/storageGroup/device" | important | 当前时间timeSeries/storageGroup/device的数量 | quantity{name="timeSeries",} 1.0 | -#### 4.3.8. 集群 +#### 4.3.11. 集群 | Metric | Tag | level | 说明 | 示例 | | ------------------------- | ------------------------------- | ------ | ------------------------------------------------------------- | ---------------------------------------------------------------------------- | diff --git a/metrics/interface/src/main/java/org/apache/iotdb/metrics/DoNothingMetricService.java b/metrics/interface/src/main/java/org/apache/iotdb/metrics/DoNothingMetricService.java index 4a53111955b1..b996b8e5edd3 100644 --- a/metrics/interface/src/main/java/org/apache/iotdb/metrics/DoNothingMetricService.java +++ b/metrics/interface/src/main/java/org/apache/iotdb/metrics/DoNothingMetricService.java @@ -27,6 +27,16 @@ protected void collectFileSystemInfo() { // do nothing } + @Override + protected void collectProcessInfo() { + // do nothing + } + + @Override + protected void collectSystemInfo() { + // do nothing + } + @Override protected void reloadProperties(ReloadLevel reloadLevel) { // do nothing diff --git a/metrics/interface/src/main/java/org/apache/iotdb/metrics/MetricService.java b/metrics/interface/src/main/java/org/apache/iotdb/metrics/MetricService.java index e69f55ff006f..10867edb5930 100644 --- a/metrics/interface/src/main/java/org/apache/iotdb/metrics/MetricService.java +++ b/metrics/interface/src/main/java/org/apache/iotdb/metrics/MetricService.java @@ -61,6 +61,7 @@ public void startService() { // do some init work metricManager.init(); // do start all reporter without first time + if (!firstInit.getAndSet(false)) { startAllReporter(); } @@ -72,6 +73,12 @@ public void startService() { logger.info("Start metric at level: " + metricConfig.getMetricLevel().name()); collectFileSystemInfo(); + + // Collect process monitoring information + collectProcessInfo(); + + // Collect system monitoring information + collectSystemInfo(); } /** Stop metric service. if is disabled, do nothing */ @@ -156,6 +163,12 @@ public void enablePredefinedMetric(PredefinedMetric metric) { /** collect file system info in metric way */ protected abstract void collectFileSystemInfo(); + /** collect process info in metric way */ + protected abstract void collectProcessInfo(); + + /** collect system hardware info in metric way */ + protected abstract void collectSystemInfo(); + /** * support hot load of some properties * diff --git a/server/src/main/java/org/apache/iotdb/db/service/metrics/Metric.java b/server/src/main/java/org/apache/iotdb/db/service/metrics/Metric.java index dd545e796313..ca6b16844faf 100644 --- a/server/src/main/java/org/apache/iotdb/db/service/metrics/Metric.java +++ b/server/src/main/java/org/apache/iotdb/db/service/metrics/Metric.java @@ -35,7 +35,25 @@ public enum Metric { CLUSTER_NODE_STATUS, CLUSTER_NODE_LEADER_COUNT, CLUSTER_ELECT, - CLUSTER_UNCOMMITTED_LOG; + CLUSTER_UNCOMMITTED_LOG, + PROCESS_CPU_LOAD, + PROCESS_CPU_TIME, + PROCESS_MAX_MEM, + PROCESS_USED_MEM, + PROCESS_TOTAL_MEM, + PROCESS_FREE_MEM, + PROCESS_THREADS_COUNT, + PROCESS_MEM_RATIO, + PROCESS_STATUS, + SYS_CPU_LOAD, + SYS_CPU_CORES, + SYS_TOTAL_PHYSICAL_MEMORY_SIZE, + SYS_FREE_PHYSICAL_MEMORY_SIZE, + SYS_TOTAL_SWAP_SPACE_SIZE, + SYS_FREE_SWAP_SPACE_SIZE, + SYS_COMMITTED_VM_SIZE, + SYS_DISK_TOTAL_SPACE, + SYS_DISK_FREE_SPACE; @Override public String toString() { diff --git a/server/src/main/java/org/apache/iotdb/db/service/metrics/MetricsService.java b/server/src/main/java/org/apache/iotdb/db/service/metrics/MetricsService.java index 88d0cc2208ad..2e8579e568ce 100644 --- a/server/src/main/java/org/apache/iotdb/db/service/metrics/MetricsService.java +++ b/server/src/main/java/org/apache/iotdb/db/service/metrics/MetricsService.java @@ -173,6 +173,25 @@ public void collectFileSystemInfo() { "unseq"); } + @Override + protected void collectProcessInfo() { + logger.info("start collecting information of metric service's process"); + ProcessMetricsMonitor processMetricsMonitor = ProcessMetricsMonitor.getInstance(); + processMetricsMonitor.collectProcessCPUInfo(); + processMetricsMonitor.collectProcessMemInfo(); + processMetricsMonitor.collectThreadInfo(); + processMetricsMonitor.collectProcessStatusInfo(); + } + + @Override + protected void collectSystemInfo() { + logger.info("start collecting information of system hardware"); + SysRunMetricsMonitor sysRunMetricsMonitor = SysRunMetricsMonitor.getInstance(); + sysRunMetricsMonitor.collectSystemCpuInfo(); + sysRunMetricsMonitor.collectSystemMEMInfo(); + sysRunMetricsMonitor.collectSystemDiskInfo(); + } + @Override public void reloadProperties(ReloadLevel reloadLevel) { logger.info("Reload properties of metric service"); diff --git a/server/src/main/java/org/apache/iotdb/db/service/metrics/ProcessMetricsMonitor.java b/server/src/main/java/org/apache/iotdb/db/service/metrics/ProcessMetricsMonitor.java new file mode 100644 index 000000000000..3e91f605b0b6 --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/service/metrics/ProcessMetricsMonitor.java @@ -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.iotdb.db.service.metrics; + +import org.apache.iotdb.metrics.MetricManager; +import org.apache.iotdb.metrics.utils.MetricLevel; + +import com.sun.management.OperatingSystemMXBean; + +import java.lang.management.ManagementFactory; + +public class ProcessMetricsMonitor { + + private MetricManager metricManager = MetricsService.getInstance().getMetricManager(); + private OperatingSystemMXBean sunOsMXBean; + private Runtime runtime; + + public void collectProcessCPUInfo() { + metricManager.getOrCreateAutoGauge( + Metric.PROCESS_CPU_LOAD.toString(), + MetricLevel.CORE, + sunOsMXBean, + a -> (long) (sunOsMXBean.getProcessCpuLoad() * 100), + Tag.NAME.toString(), + "process"); + + metricManager.getOrCreateAutoGauge( + Metric.PROCESS_CPU_TIME.toString(), + MetricLevel.CORE, + sunOsMXBean, + com.sun.management.OperatingSystemMXBean::getProcessCpuTime, + Tag.NAME.toString(), + "process"); + } + + public void collectProcessMemInfo() { + Runtime runtime = Runtime.getRuntime(); + metricManager.getOrCreateAutoGauge( + Metric.PROCESS_MAX_MEM.toString(), + MetricLevel.CORE, + runtime, + a -> runtime.maxMemory(), + Tag.NAME.toString(), + "process"); + metricManager.getOrCreateAutoGauge( + Metric.PROCESS_TOTAL_MEM.toString(), + MetricLevel.CORE, + runtime, + a -> runtime.totalMemory(), + Tag.NAME.toString(), + "process"); + metricManager.getOrCreateAutoGauge( + Metric.PROCESS_FREE_MEM.toString(), + MetricLevel.CORE, + runtime, + a -> runtime.freeMemory(), + Tag.NAME.toString(), + "process"); + metricManager.getOrCreateAutoGauge( + Metric.PROCESS_USED_MEM.toString(), + MetricLevel.CORE, + this, + a -> getProcessUsedMemory(), + Tag.NAME.toString(), + "process"); + metricManager.getOrCreateAutoGauge( + Metric.PROCESS_MEM_RATIO.toString(), + MetricLevel.CORE, + this, + a -> Math.round(getProcessMemoryRatio()), + Tag.NAME.toString(), + "process"); + } + + public void collectThreadInfo() { + metricManager.getOrCreateAutoGauge( + Metric.PROCESS_THREADS_COUNT.toString(), + MetricLevel.CORE, + this, + a -> getThreadsCount(), + Tag.NAME.toString(), + "process"); + } + + public void collectProcessStatusInfo() { + metricManager.getOrCreateAutoGauge( + Metric.PROCESS_STATUS.toString(), + MetricLevel.CORE, + this, + a -> (getProcessStatus()), + Tag.NAME.toString(), + "process"); + } + + private ProcessMetricsMonitor() { + sunOsMXBean = + (com.sun.management.OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean(); + runtime = Runtime.getRuntime(); + } + + private long getProcessUsedMemory() { + return runtime.totalMemory() - runtime.freeMemory(); + } + + private long getProcessStatus() { + return Thread.currentThread().isAlive() ? 1 : 0; + } + + private int getThreadsCount() { + ThreadGroup parentThread; + for (parentThread = Thread.currentThread().getThreadGroup(); + parentThread.getParent() != null; + parentThread = parentThread.getParent()) {} + + return parentThread.activeCount(); + } + + private double getProcessMemoryRatio() { + long processUsedMemory = getProcessUsedMemory(); + long totalPhysicalMemorySize = sunOsMXBean.getTotalPhysicalMemorySize(); + return (double) processUsedMemory / (double) totalPhysicalMemorySize * 100; + } + + public static ProcessMetricsMonitor getInstance() { + return ThreadMetricsMonitorHolder.INSTANCE; + } + + private static class ThreadMetricsMonitorHolder { + private static final ProcessMetricsMonitor INSTANCE = new ProcessMetricsMonitor(); + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/service/metrics/SysRunMetricsMonitor.java b/server/src/main/java/org/apache/iotdb/db/service/metrics/SysRunMetricsMonitor.java new file mode 100644 index 000000000000..e7ccea7450bd --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/service/metrics/SysRunMetricsMonitor.java @@ -0,0 +1,134 @@ +/* + * 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.iotdb.db.service.metrics; + +import org.apache.iotdb.db.conf.IoTDBDescriptor; +import org.apache.iotdb.metrics.MetricManager; +import org.apache.iotdb.metrics.utils.MetricLevel; + +import com.sun.management.OperatingSystemMXBean; + +import java.io.File; +import java.lang.management.ManagementFactory; + +public class SysRunMetricsMonitor { + private MetricManager metricManager = MetricsService.getInstance().getMetricManager(); + private com.sun.management.OperatingSystemMXBean osMXBean; + + private SysRunMetricsMonitor() { + osMXBean = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean(); + } + + public void collectSystemCpuInfo() { + metricManager.getOrCreateAutoGauge( + Metric.SYS_CPU_LOAD.toString(), + MetricLevel.CORE, + osMXBean, + a -> (long) (osMXBean.getSystemCpuLoad() * 100), + Tag.NAME.toString(), + "system"); + + metricManager + .getOrCreateGauge( + Metric.SYS_CPU_CORES.toString(), MetricLevel.IMPORTANT, Tag.NAME.toString(), "system") + .set(osMXBean.getAvailableProcessors()); + } + + public void collectSystemMEMInfo() { + metricManager + .getOrCreateGauge( + Metric.SYS_TOTAL_PHYSICAL_MEMORY_SIZE.toString(), + MetricLevel.CORE, + Tag.NAME.toString(), + "system") + .set(osMXBean.getTotalPhysicalMemorySize()); + metricManager.getOrCreateAutoGauge( + Metric.SYS_FREE_PHYSICAL_MEMORY_SIZE.toString(), + MetricLevel.CORE, + osMXBean, + a -> osMXBean.getFreePhysicalMemorySize(), + Tag.NAME.toString(), + "system"); + metricManager.getOrCreateAutoGauge( + Metric.SYS_TOTAL_SWAP_SPACE_SIZE.toString(), + MetricLevel.CORE, + osMXBean, + a -> osMXBean.getTotalSwapSpaceSize(), + Tag.NAME.toString(), + "system"); + metricManager.getOrCreateAutoGauge( + Metric.SYS_FREE_SWAP_SPACE_SIZE.toString(), + MetricLevel.CORE, + osMXBean, + a -> osMXBean.getFreeSwapSpaceSize(), + Tag.NAME.toString(), + "system"); + metricManager.getOrCreateAutoGauge( + Metric.SYS_COMMITTED_VM_SIZE.toString(), + MetricLevel.CORE, + osMXBean, + a -> osMXBean.getCommittedVirtualMemorySize(), + Tag.NAME.toString(), + "system"); + } + + public void collectSystemDiskInfo() { + metricManager.getOrCreateAutoGauge( + Metric.SYS_DISK_TOTAL_SPACE.toString(), + MetricLevel.CORE, + this, + a -> getSysDiskTotalSpace(), + Tag.NAME.toString(), + "system"); + metricManager.getOrCreateAutoGauge( + Metric.SYS_DISK_FREE_SPACE.toString(), + MetricLevel.CORE, + this, + a -> getSysDickFreeSpace(), + Tag.NAME.toString(), + "system"); + String[] dataDirs = IoTDBDescriptor.getInstance().getConfig().getDataDirs(); + } + + private long getSysDiskTotalSpace() { + File[] files = File.listRoots(); + long sysTotalSpace = 0L; + for (File file : files) { + sysTotalSpace += file.getTotalSpace(); + } + return sysTotalSpace; + } + + private long getSysDickFreeSpace() { + File[] files = File.listRoots(); + long sysFreeSpace = 0L; + for (File file : files) { + sysFreeSpace += file.getFreeSpace(); + } + return sysFreeSpace; + } + + public static SysRunMetricsMonitor getInstance() { + return SysRunMetricsMonitor.SysRunMetricsMonitorHolder.INSTANCE; + } + + private static class SysRunMetricsMonitorHolder { + private static final SysRunMetricsMonitor INSTANCE = new SysRunMetricsMonitor(); + } +} From 434004fc63df6879ba7b51b4ba35eee2e5560630 Mon Sep 17 00:00:00 2001 From: Mrquan <50790061+MrQuansy@users.noreply.github.com> Date: Tue, 17 May 2022 10:34:49 +0800 Subject: [PATCH 025/436] [IOTDB-3092] Pooling config node clients using ClientManager (#5847) --- .../assembly/resources/sbin/stop-datanode.bat | 2 + .../iotdb/db/auth/AuthorityChecker.java | 31 +- .../iotdb/db/client/ConfigNodeClient.java | 323 ++++++++++++++---- .../iotdb/db/client/ConfigNodeInfo.java | 158 +++++++++ .../db/client/DataNodeClientPoolFactory.java | 18 + .../apache/iotdb/db/conf/IoTDBStartCheck.java | 38 --- .../apache/iotdb/db/mpp/plan/Coordinator.java | 8 +- .../plan/analyze/ClusterPartitionFetcher.java | 51 +-- .../config/AuthorizerConfigTask.java | 37 +- .../execution/config/ConfigExecution.java | 14 +- .../config/CountStorageGroupTask.java | 16 +- .../config/DeleteStorageGroupTask.java | 17 +- .../plan/execution/config/IConfigTask.java | 8 +- .../execution/config/SetStorageGroupTask.java | 19 +- .../mpp/plan/execution/config/SetTTLTask.java | 20 +- .../config/ShowStorageGroupTask.java | 20 +- .../plan/execution/config/ShowTTLTask.java | 20 +- .../org/apache/iotdb/db/service/DataNode.java | 15 +- .../db/mpp/execution/ConfigExecutionTest.java | 16 +- 19 files changed, 610 insertions(+), 221 deletions(-) create mode 100644 server/src/main/java/org/apache/iotdb/db/client/ConfigNodeInfo.java diff --git a/server/src/assembly/resources/sbin/stop-datanode.bat b/server/src/assembly/resources/sbin/stop-datanode.bat index f796ff052803..e33c1bd81f1b 100644 --- a/server/src/assembly/resources/sbin/stop-datanode.bat +++ b/server/src/assembly/resources/sbin/stop-datanode.bat @@ -25,3 +25,5 @@ popd set exec_dir=%exec_dir:\=\\% wmic process where (commandline like "%%iotdb.DataNode%%" and not name="wmic.exe" and commandline like "%%%exec_dir%%%") delete + + diff --git a/server/src/main/java/org/apache/iotdb/db/auth/AuthorityChecker.java b/server/src/main/java/org/apache/iotdb/db/auth/AuthorityChecker.java index d2a01d1a723f..2966953dec97 100644 --- a/server/src/main/java/org/apache/iotdb/db/auth/AuthorityChecker.java +++ b/server/src/main/java/org/apache/iotdb/db/auth/AuthorityChecker.java @@ -21,12 +21,16 @@ import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.commons.auth.AuthException; import org.apache.iotdb.commons.auth.entity.PrivilegeType; +import org.apache.iotdb.commons.client.IClientManager; import org.apache.iotdb.commons.conf.CommonDescriptor; +import org.apache.iotdb.commons.consensus.PartitionRegionId; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.commons.utils.AuthUtils; import org.apache.iotdb.confignode.rpc.thrift.TCheckUserPrivilegesReq; import org.apache.iotdb.confignode.rpc.thrift.TLoginReq; import org.apache.iotdb.db.client.ConfigNodeClient; +import org.apache.iotdb.db.client.ConfigNodeInfo; +import org.apache.iotdb.db.client.DataNodeClientPoolFactory; import org.apache.iotdb.db.conf.OperationType; import org.apache.iotdb.db.mpp.plan.constant.StatementType; import org.apache.iotdb.db.mpp.plan.statement.Statement; @@ -34,13 +38,14 @@ import org.apache.iotdb.db.qp.logical.Operator; import org.apache.iotdb.db.query.control.SessionManager; import org.apache.iotdb.rpc.ConfigNodeConnectionException; -import org.apache.iotdb.rpc.IoTDBConnectionException; import org.apache.iotdb.rpc.RpcUtils; import org.apache.iotdb.rpc.TSStatusCode; +import org.apache.thrift.TException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -55,6 +60,10 @@ public class AuthorityChecker { private static AuthorizerManager authorizerManager = AuthorizerManager.getInstance(); private static SessionManager sessionManager = SessionManager.getInstance(); + private static final IClientManager configNodeClientManager = + new IClientManager.Factory() + .createClientManager(new DataNodeClientPoolFactory.ConfigNodeClientPoolFactory()); + private AuthorityChecker() {} /** @@ -157,17 +166,13 @@ private static boolean checkOnePath(String username, PartialPath path, int permi public static TSStatus checkUser(String username, String password) { TLoginReq req = new TLoginReq(username, password); TSStatus status = null; - ConfigNodeClient configNodeClient = null; - try { - configNodeClient = new ConfigNodeClient(); + try (ConfigNodeClient configNodeClient = + configNodeClientManager.borrowClient(ConfigNodeInfo.partitionRegionId)) { // Send request to some API server status = configNodeClient.login(req); - } catch (IoTDBConnectionException e) { + } catch (TException | IOException e) { throw new ConfigNodeConnectionException("Couldn't connect config node"); } finally { - if (configNodeClient != null) { - configNodeClient.close(); - } if (status == null) { status = new TSStatus(); } @@ -209,18 +214,14 @@ public static boolean checkAuthorization(Statement statement, String username) public static TSStatus checkPath(String username, List allPath, int permission) { TCheckUserPrivilegesReq req = new TCheckUserPrivilegesReq(username, allPath, permission); - ConfigNodeClient configNodeClient = null; TSStatus status = null; - try { - configNodeClient = new ConfigNodeClient(); + try (ConfigNodeClient configNodeClient = + configNodeClientManager.borrowClient(ConfigNodeInfo.partitionRegionId)) { // Send request to some API server status = configNodeClient.checkUserPrivileges(req); - } catch (IoTDBConnectionException e) { + } catch (TException | IOException e) { throw new ConfigNodeConnectionException("Couldn't connect config node"); } finally { - if (configNodeClient != null) { - configNodeClient.close(); - } if (status == null) { status = new TSStatus(); } diff --git a/server/src/main/java/org/apache/iotdb/db/client/ConfigNodeClient.java b/server/src/main/java/org/apache/iotdb/db/client/ConfigNodeClient.java index a5d34a576f82..f1494eeb85a3 100644 --- a/server/src/main/java/org/apache/iotdb/db/client/ConfigNodeClient.java +++ b/server/src/main/java/org/apache/iotdb/db/client/ConfigNodeClient.java @@ -22,49 +22,65 @@ import org.apache.iotdb.common.rpc.thrift.TConfigNodeLocation; import org.apache.iotdb.common.rpc.thrift.TEndPoint; import org.apache.iotdb.common.rpc.thrift.TSStatus; +import org.apache.iotdb.commons.client.BaseClientFactory; +import org.apache.iotdb.commons.client.ClientFactoryProperty; +import org.apache.iotdb.commons.client.ClientManager; +import org.apache.iotdb.commons.client.ClientPoolProperty; +import org.apache.iotdb.commons.client.sync.SyncThriftClient; +import org.apache.iotdb.commons.client.sync.SyncThriftClientWithErrorHandler; +import org.apache.iotdb.commons.consensus.PartitionRegionId; import org.apache.iotdb.confignode.rpc.thrift.ConfigIService; import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerReq; import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerResp; import org.apache.iotdb.confignode.rpc.thrift.TCheckUserPrivilegesReq; +import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeRegisterReq; +import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeRegisterResp; import org.apache.iotdb.confignode.rpc.thrift.TCountStorageGroupResp; import org.apache.iotdb.confignode.rpc.thrift.TDataNodeInfoResp; import org.apache.iotdb.confignode.rpc.thrift.TDataNodeRegisterReq; import org.apache.iotdb.confignode.rpc.thrift.TDataNodeRegisterResp; import org.apache.iotdb.confignode.rpc.thrift.TDataPartitionReq; import org.apache.iotdb.confignode.rpc.thrift.TDataPartitionResp; +import org.apache.iotdb.confignode.rpc.thrift.TDeleteStorageGroupReq; import org.apache.iotdb.confignode.rpc.thrift.TDeleteStorageGroupsReq; import org.apache.iotdb.confignode.rpc.thrift.TLoginReq; import org.apache.iotdb.confignode.rpc.thrift.TSchemaPartitionReq; import org.apache.iotdb.confignode.rpc.thrift.TSchemaPartitionResp; +import org.apache.iotdb.confignode.rpc.thrift.TSetDataReplicationFactorReq; +import org.apache.iotdb.confignode.rpc.thrift.TSetSchemaReplicationFactorReq; import org.apache.iotdb.confignode.rpc.thrift.TSetStorageGroupReq; import org.apache.iotdb.confignode.rpc.thrift.TSetTTLReq; +import org.apache.iotdb.confignode.rpc.thrift.TSetTimePartitionIntervalReq; import org.apache.iotdb.confignode.rpc.thrift.TStorageGroupSchemaResp; import org.apache.iotdb.db.conf.IoTDBDescriptor; -import org.apache.iotdb.rpc.IoTDBConnectionException; import org.apache.iotdb.rpc.RpcTransportFactory; import org.apache.iotdb.rpc.TSStatusCode; +import org.apache.commons.pool2.PooledObject; +import org.apache.commons.pool2.impl.DefaultPooledObject; import org.apache.thrift.TException; import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.thrift.protocol.TCompactProtocol; +import org.apache.thrift.protocol.TProtocolFactory; import org.apache.thrift.transport.TTransport; import org.apache.thrift.transport.TTransportException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.List; -public class ConfigNodeClient { +public class ConfigNodeClient implements ConfigIService.Iface, SyncThriftClient, AutoCloseable { private static final Logger logger = LoggerFactory.getLogger(ConfigNodeClient.class); - private static final int TIMEOUT_MS = 10000; - private static final int RETRY_NUM = 5; public static final String MSG_RECONNECTION_FAIL = "Fail to connect to any config node. Please check server it"; + private long connectionTimeout = ClientPoolProperty.DefaultProperty.WAIT_CLIENT_TIMEOUT_MS; + private ConfigIService.Iface client; private TTransport transport; @@ -75,52 +91,70 @@ public class ConfigNodeClient { private int cursor = 0; - public ConfigNodeClient() throws IoTDBConnectionException { + ClientManager clientManager; + + PartitionRegionId partitionRegionId = ConfigNodeInfo.partitionRegionId; + + TProtocolFactory protocolFactory; + + public ConfigNodeClient() throws TException { // Read config nodes from configuration - configNodes = IoTDBDescriptor.getInstance().getConfig().getConfigNodeList(); - init(); - } + configNodes = ConfigNodeInfo.getInstance().getLatestConfigNodes(); + protocolFactory = + IoTDBDescriptor.getInstance().getConfig().isRpcThriftCompressionEnable() + ? new TCompactProtocol.Factory() + : new TBinaryProtocol.Factory(); - public ConfigNodeClient(List configNodes) throws IoTDBConnectionException { - this.configNodes = configNodes; init(); } - public ConfigNodeClient(List configNodes, TEndPoint configLeader) - throws IoTDBConnectionException { - this.configNodes = configNodes; - this.configLeader = configLeader; + public ConfigNodeClient( + TProtocolFactory protocolFactory, + long connectionTimeout, + ClientManager clientManager) + throws TException { + configNodes = ConfigNodeInfo.getInstance().getLatestConfigNodes(); + this.protocolFactory = protocolFactory; + this.connectionTimeout = connectionTimeout; + this.clientManager = clientManager; + init(); } - public void init() throws IoTDBConnectionException { + public void init() throws TException { reconnect(); } - public void connect(TEndPoint endpoint) throws IoTDBConnectionException { + public void connect(TEndPoint endpoint) throws TException { try { transport = RpcTransportFactory.INSTANCE.getTransport( // as there is a try-catch already, we do not need to use TSocket.wrap - endpoint.getIp(), endpoint.getPort(), TIMEOUT_MS); + endpoint.getIp(), endpoint.getPort(), (int) connectionTimeout); transport.open(); } catch (TTransportException e) { - throw new IoTDBConnectionException(e); + throw new TException(e); } - if (IoTDBDescriptor.getInstance().getConfig().isRpcThriftCompressionEnable()) { - client = new ConfigIService.Client(new TCompactProtocol(transport)); - } else { - client = new ConfigIService.Client(new TBinaryProtocol(transport)); + client = new ConfigIService.Client(protocolFactory.getProtocol(transport)); + } + + private void reconnect() throws TException { + try { + tryToConnect(); + } catch (TException e) { + // can not connect to each config node + syncLatestConfigNodeList(); + tryToConnect(); } } - private void reconnect() throws IoTDBConnectionException { + private void tryToConnect() throws TException { if (configLeader != null) { try { connect(configLeader); return; - } catch (IoTDBConnectionException e) { + } catch (TException e) { logger.warn("The current node may have been down {},try next node", configLeader); configLeader = null; } @@ -137,18 +171,42 @@ private void reconnect() throws IoTDBConnectionException { try { connect(tryEndpoint); return; - } catch (IoTDBConnectionException e) { + } catch (TException e) { logger.warn("The current node may have been down {},try next node", tryEndpoint); } } - throw new IoTDBConnectionException(MSG_RECONNECTION_FAIL); + throw new TException(MSG_RECONNECTION_FAIL); } + public TTransport getTransport() { + return transport; + } + + public void syncLatestConfigNodeList() { + configNodes = ConfigNodeInfo.getInstance().getLatestConfigNodes(); + cursor = 0; + } + + @Override public void close() { + if (clientManager != null) { + clientManager.returnClient(partitionRegionId, this); + } else { + invalidate(); + } + } + + @Override + public void invalidate() { transport.close(); } + @Override + public void invalidateAll() { + clientManager.clear(ConfigNodeInfo.partitionRegionId); + } + private boolean updateConfigNodeLeader(TSStatus status) { if (status.getCode() == TSStatusCode.NEED_REDIRECTION.getStatusCode()) { if (status.isSetRedirectNode()) { @@ -162,8 +220,8 @@ private boolean updateConfigNodeLeader(TSStatus status) { return false; } - public TDataNodeRegisterResp registerDataNode(TDataNodeRegisterReq req) - throws IoTDBConnectionException { + @Override + public TDataNodeRegisterResp registerDataNode(TDataNodeRegisterReq req) throws TException { for (int i = 0; i < RETRY_NUM; i++) { try { TDataNodeRegisterResp resp = client.registerDataNode(req); @@ -183,13 +241,14 @@ public TDataNodeRegisterResp registerDataNode(TDataNodeRegisterReq req) } reconnect(); } - throw new IoTDBConnectionException(MSG_RECONNECTION_FAIL); + throw new TException(MSG_RECONNECTION_FAIL); } - public TDataNodeInfoResp getDataNodeInfos(int dataNodeID) throws IoTDBConnectionException { + @Override + public TDataNodeInfoResp getDataNodeInfo(int dataNodeId) throws TException { for (int i = 0; i < RETRY_NUM; i++) { try { - TDataNodeInfoResp resp = client.getDataNodeInfo(dataNodeID); + TDataNodeInfoResp resp = client.getDataNodeInfo(dataNodeId); if (!updateConfigNodeLeader(resp.status)) { return resp; } @@ -198,10 +257,11 @@ public TDataNodeInfoResp getDataNodeInfos(int dataNodeID) throws IoTDBConnection } reconnect(); } - throw new IoTDBConnectionException(MSG_RECONNECTION_FAIL); + throw new TException(MSG_RECONNECTION_FAIL); } - public TSStatus setStorageGroup(TSetStorageGroupReq req) throws IoTDBConnectionException { + @Override + public TSStatus setStorageGroup(TSetStorageGroupReq req) throws TException { for (int i = 0; i < RETRY_NUM; i++) { try { TSStatus status = client.setStorageGroup(req); @@ -213,10 +273,27 @@ public TSStatus setStorageGroup(TSetStorageGroupReq req) throws IoTDBConnectionE } reconnect(); } - throw new IoTDBConnectionException(MSG_RECONNECTION_FAIL); + throw new TException(MSG_RECONNECTION_FAIL); } - public TSStatus deleteStorageGroups(TDeleteStorageGroupsReq req) throws IoTDBConnectionException { + @Override + public TSStatus deleteStorageGroup(TDeleteStorageGroupReq req) throws TException { + for (int i = 0; i < RETRY_NUM; i++) { + try { + TSStatus status = client.deleteStorageGroup(req); + if (!updateConfigNodeLeader(status)) { + return status; + } + } catch (TException e) { + configLeader = null; + } + reconnect(); + } + throw new TException(MSG_RECONNECTION_FAIL); + } + + @Override + public TSStatus deleteStorageGroups(TDeleteStorageGroupsReq req) throws TException { for (int i = 0; i < RETRY_NUM; i++) { try { TSStatus status = client.deleteStorageGroups(req); @@ -228,11 +305,12 @@ public TSStatus deleteStorageGroups(TDeleteStorageGroupsReq req) throws IoTDBCon } reconnect(); } - throw new IoTDBConnectionException(MSG_RECONNECTION_FAIL); + throw new TException(MSG_RECONNECTION_FAIL); } + @Override public TCountStorageGroupResp countMatchedStorageGroups(List storageGroupPathPattern) - throws IoTDBConnectionException { + throws TException { for (int i = 0; i < RETRY_NUM; i++) { try { TCountStorageGroupResp resp = client.countMatchedStorageGroups(storageGroupPathPattern); @@ -244,11 +322,12 @@ public TCountStorageGroupResp countMatchedStorageGroups(List storageGrou } reconnect(); } - throw new IoTDBConnectionException(MSG_RECONNECTION_FAIL); + throw new TException(MSG_RECONNECTION_FAIL); } + @Override public TStorageGroupSchemaResp getMatchedStorageGroupSchemas(List storageGroupPathPattern) - throws IoTDBConnectionException { + throws TException { for (int i = 0; i < RETRY_NUM; i++) { try { TStorageGroupSchemaResp resp = @@ -261,10 +340,11 @@ public TStorageGroupSchemaResp getMatchedStorageGroupSchemas(List storag } reconnect(); } - throw new IoTDBConnectionException(MSG_RECONNECTION_FAIL); + throw new TException(MSG_RECONNECTION_FAIL); } - public TSStatus setTTL(TSetTTLReq setTTLReq) throws IoTDBConnectionException { + @Override + public TSStatus setTTL(TSetTTLReq setTTLReq) throws TException { for (int i = 0; i < RETRY_NUM; i++) { try { TSStatus status = client.setTTL(setTTLReq); @@ -276,11 +356,59 @@ public TSStatus setTTL(TSetTTLReq setTTLReq) throws IoTDBConnectionException { } reconnect(); } - throw new IoTDBConnectionException(MSG_RECONNECTION_FAIL); + throw new TException(MSG_RECONNECTION_FAIL); + } + + @Override + public TSStatus setSchemaReplicationFactor(TSetSchemaReplicationFactorReq req) throws TException { + for (int i = 0; i < RETRY_NUM; i++) { + try { + TSStatus status = client.setSchemaReplicationFactor(req); + if (!updateConfigNodeLeader(status)) { + return status; + } + } catch (TException e) { + configLeader = null; + } + reconnect(); + } + throw new TException(MSG_RECONNECTION_FAIL); + } + + @Override + public TSStatus setDataReplicationFactor(TSetDataReplicationFactorReq req) throws TException { + for (int i = 0; i < RETRY_NUM; i++) { + try { + TSStatus status = client.setDataReplicationFactor(req); + if (!updateConfigNodeLeader(status)) { + return status; + } + } catch (TException e) { + configLeader = null; + } + reconnect(); + } + throw new TException(MSG_RECONNECTION_FAIL); } - public TSchemaPartitionResp getSchemaPartition(TSchemaPartitionReq req) - throws IoTDBConnectionException { + @Override + public TSStatus setTimePartitionInterval(TSetTimePartitionIntervalReq req) throws TException { + for (int i = 0; i < RETRY_NUM; i++) { + try { + TSStatus status = client.setTimePartitionInterval(req); + if (!updateConfigNodeLeader(status)) { + return status; + } + } catch (TException e) { + configLeader = null; + } + reconnect(); + } + throw new TException(MSG_RECONNECTION_FAIL); + } + + @Override + public TSchemaPartitionResp getSchemaPartition(TSchemaPartitionReq req) throws TException { for (int i = 0; i < RETRY_NUM; i++) { try { TSchemaPartitionResp resp = client.getSchemaPartition(req); @@ -292,11 +420,12 @@ public TSchemaPartitionResp getSchemaPartition(TSchemaPartitionReq req) } reconnect(); } - throw new IoTDBConnectionException(MSG_RECONNECTION_FAIL); + throw new TException(MSG_RECONNECTION_FAIL); } + @Override public TSchemaPartitionResp getOrCreateSchemaPartition(TSchemaPartitionReq req) - throws IoTDBConnectionException { + throws TException { for (int i = 0; i < RETRY_NUM; i++) { try { TSchemaPartitionResp resp = client.getOrCreateSchemaPartition(req); @@ -308,11 +437,11 @@ public TSchemaPartitionResp getOrCreateSchemaPartition(TSchemaPartitionReq req) } reconnect(); } - throw new IoTDBConnectionException(MSG_RECONNECTION_FAIL); + throw new TException(MSG_RECONNECTION_FAIL); } - public TDataPartitionResp getDataPartition(TDataPartitionReq req) - throws IoTDBConnectionException { + @Override + public TDataPartitionResp getDataPartition(TDataPartitionReq req) throws TException { for (int i = 0; i < RETRY_NUM; i++) { try { TDataPartitionResp resp = client.getDataPartition(req); @@ -324,11 +453,11 @@ public TDataPartitionResp getDataPartition(TDataPartitionReq req) } reconnect(); } - throw new IoTDBConnectionException(MSG_RECONNECTION_FAIL); + throw new TException(MSG_RECONNECTION_FAIL); } - public TDataPartitionResp getOrCreateDataPartition(TDataPartitionReq req) - throws IoTDBConnectionException { + @Override + public TDataPartitionResp getOrCreateDataPartition(TDataPartitionReq req) throws TException { for (int i = 0; i < RETRY_NUM; i++) { try { TDataPartitionResp resp = client.getOrCreateDataPartition(req); @@ -340,10 +469,11 @@ public TDataPartitionResp getOrCreateDataPartition(TDataPartitionReq req) } reconnect(); } - throw new IoTDBConnectionException(MSG_RECONNECTION_FAIL); + throw new TException(MSG_RECONNECTION_FAIL); } - public TSStatus operatePermission(TAuthorizerReq req) throws IoTDBConnectionException { + @Override + public TSStatus operatePermission(TAuthorizerReq req) throws TException { for (int i = 0; i < RETRY_NUM; i++) { try { TSStatus status = client.operatePermission(req); @@ -355,10 +485,11 @@ public TSStatus operatePermission(TAuthorizerReq req) throws IoTDBConnectionExce } reconnect(); } - throw new IoTDBConnectionException(MSG_RECONNECTION_FAIL); + throw new TException(MSG_RECONNECTION_FAIL); } - public TAuthorizerResp queryPermission(TAuthorizerReq req) throws IoTDBConnectionException { + @Override + public TAuthorizerResp queryPermission(TAuthorizerReq req) throws TException { for (int i = 0; i < RETRY_NUM; i++) { try { TAuthorizerResp resp = client.queryPermission(req); @@ -370,10 +501,11 @@ public TAuthorizerResp queryPermission(TAuthorizerReq req) throws IoTDBConnectio } reconnect(); } - throw new IoTDBConnectionException(MSG_RECONNECTION_FAIL); + throw new TException(MSG_RECONNECTION_FAIL); } - public TSStatus login(TLoginReq req) throws IoTDBConnectionException { + @Override + public TSStatus login(TLoginReq req) throws TException { for (int i = 0; i < RETRY_NUM; i++) { try { TSStatus status = client.login(req); @@ -385,10 +517,11 @@ public TSStatus login(TLoginReq req) throws IoTDBConnectionException { } reconnect(); } - throw new IoTDBConnectionException(MSG_RECONNECTION_FAIL); + throw new TException(MSG_RECONNECTION_FAIL); } - public TSStatus checkUserPrivileges(TCheckUserPrivilegesReq req) throws IoTDBConnectionException { + @Override + public TSStatus checkUserPrivileges(TCheckUserPrivilegesReq req) throws TException { for (int i = 0; i < RETRY_NUM; i++) { try { TSStatus status = client.checkUserPrivileges(req); @@ -400,6 +533,74 @@ public TSStatus checkUserPrivileges(TCheckUserPrivilegesReq req) throws IoTDBCon } reconnect(); } - throw new IoTDBConnectionException(MSG_RECONNECTION_FAIL); + throw new TException(MSG_RECONNECTION_FAIL); + } + + @Override + public TConfigNodeRegisterResp registerConfigNode(TConfigNodeRegisterReq req) throws TException { + for (int i = 0; i < RETRY_NUM; i++) { + try { + TConfigNodeRegisterResp resp = client.registerConfigNode(req); + if (!updateConfigNodeLeader(resp.status)) { + return resp; + } + } catch (TException e) { + configLeader = null; + } + reconnect(); + } + throw new TException(MSG_RECONNECTION_FAIL); + } + + @Override + public TSStatus applyConfigNode(TConfigNodeLocation configNodeLocation) throws TException { + for (int i = 0; i < RETRY_NUM; i++) { + try { + TSStatus status = client.applyConfigNode(configNodeLocation); + if (!updateConfigNodeLeader(status)) { + return status; + } + } catch (TException e) { + configLeader = null; + } + reconnect(); + } + throw new TException(MSG_RECONNECTION_FAIL); + } + + public static class Factory extends BaseClientFactory { + + public Factory( + ClientManager clientManager, + ClientFactoryProperty clientFactoryProperty) { + super(clientManager, clientFactoryProperty); + } + + @Override + public void destroyObject( + PartitionRegionId partitionRegionId, PooledObject pooledObject) { + pooledObject.getObject().invalidate(); + } + + @Override + public PooledObject makeObject(PartitionRegionId partitionRegionId) + throws Exception { + Constructor constructor = + ConfigNodeClient.class.getConstructor( + TProtocolFactory.class, long.class, clientManager.getClass()); + return new DefaultPooledObject<>( + SyncThriftClientWithErrorHandler.newErrorHandler( + ConfigNodeClient.class, + constructor, + clientFactoryProperty.getProtocolFactory(), + clientFactoryProperty.getConnectionTimeoutMs(), + clientManager)); + } + + @Override + public boolean validateObject( + PartitionRegionId partitionRegionId, PooledObject pooledObject) { + return pooledObject.getObject() != null && pooledObject.getObject().getTransport().isOpen(); + } } } diff --git a/server/src/main/java/org/apache/iotdb/db/client/ConfigNodeInfo.java b/server/src/main/java/org/apache/iotdb/db/client/ConfigNodeInfo.java new file mode 100644 index 000000000000..e98efb15ec38 --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/client/ConfigNodeInfo.java @@ -0,0 +1,158 @@ +/* + * 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.iotdb.db.client; + +import org.apache.iotdb.common.rpc.thrift.TEndPoint; +import org.apache.iotdb.commons.consensus.PartitionRegionId; +import org.apache.iotdb.commons.exception.BadNodeUrlException; +import org.apache.iotdb.commons.file.SystemFileFactory; +import org.apache.iotdb.commons.utils.NodeUrlUtils; +import org.apache.iotdb.db.conf.IoTDBDescriptor; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +public class ConfigNodeInfo { + + private static final Logger logger = LoggerFactory.getLogger(ConfigNodeInfo.class); + + private final String CONFIG_NODE_LIST = "config_node_list"; + + private static final String PROPERTIES_FILE_NAME = "system.properties"; + + private final ReentrantReadWriteLock configNodeInfoReadWriteLock; + + // latest config nodes + private final Set onlineConfigNodes; + + public static PartitionRegionId partitionRegionId = new PartitionRegionId(0); + + private File propertiesFile; + + private ConfigNodeInfo() { + this.configNodeInfoReadWriteLock = new ReentrantReadWriteLock(); + this.onlineConfigNodes = new HashSet<>(); + propertiesFile = + SystemFileFactory.INSTANCE.getFile( + IoTDBDescriptor.getInstance().getConfig().getSchemaDir() + + File.separator + + PROPERTIES_FILE_NAME); + } + + /** + * Update ConfigNodeList both in memory and confignode-system.properties file + * + * @param latestConfigNodes + */ + public void updateConfigNodeList(List latestConfigNodes) { + // check whether the config nodes are latest or not + configNodeInfoReadWriteLock.readLock().lock(); + try { + if (onlineConfigNodes.containsAll(latestConfigNodes) + && new HashSet<>(latestConfigNodes).containsAll(onlineConfigNodes)) { + return; + } + } finally { + configNodeInfoReadWriteLock.readLock().unlock(); + } + + // update config nodes + configNodeInfoReadWriteLock.writeLock().lock(); + try { + onlineConfigNodes.clear(); + onlineConfigNodes.addAll(latestConfigNodes); + storeConfigNode(); + + logger.info("Successfully update ConfigNode: {}.", onlineConfigNodes); + } catch (IOException e) { + logger.error("Update ConfigNode failed.", e); + } finally { + configNodeInfoReadWriteLock.writeLock().unlock(); + } + } + + /** call this method to store config node list */ + private void storeConfigNode() throws IOException { + Properties properties = new Properties(); + try (FileInputStream inputStream = new FileInputStream(propertiesFile)) { + properties.load(inputStream); + } + properties.setProperty( + CONFIG_NODE_LIST, NodeUrlUtils.convertTEndPointUrls(new ArrayList<>(onlineConfigNodes))); + properties.store(new FileOutputStream(propertiesFile), ""); + } + + public void loadConfigNodeList() { + // properties contain CONFIG_NODE_LIST only when start as Data node + try { + configNodeInfoReadWriteLock.writeLock().lock(); + Properties properties = new Properties(); + try (FileInputStream inputStream = new FileInputStream(propertiesFile)) { + properties.load(inputStream); + } catch (IOException e) { + throw new RuntimeException(e); + } + + if (properties.containsKey(CONFIG_NODE_LIST)) { + onlineConfigNodes.clear(); + onlineConfigNodes.addAll( + NodeUrlUtils.parseTEndPointUrls(properties.getProperty(CONFIG_NODE_LIST))); + } + } catch (BadNodeUrlException e) { + logger.error("Cannot parse config node list in system.properties"); + } finally { + configNodeInfoReadWriteLock.writeLock().unlock(); + } + } + + public List getLatestConfigNodes() { + List result; + configNodeInfoReadWriteLock.readLock().lock(); + try { + result = new ArrayList<>(onlineConfigNodes); + } finally { + configNodeInfoReadWriteLock.readLock().unlock(); + } + return result; + } + + private static class ConfigNodeInfoHolder { + private static final ConfigNodeInfo INSTANCE = new ConfigNodeInfo(); + + private ConfigNodeInfoHolder() { + // empty constructor + } + } + + public static ConfigNodeInfo getInstance() { + return ConfigNodeInfoHolder.INSTANCE; + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/client/DataNodeClientPoolFactory.java b/server/src/main/java/org/apache/iotdb/db/client/DataNodeClientPoolFactory.java index 71d1249886ae..551d021bff71 100644 --- a/server/src/main/java/org/apache/iotdb/db/client/DataNodeClientPoolFactory.java +++ b/server/src/main/java/org/apache/iotdb/db/client/DataNodeClientPoolFactory.java @@ -30,6 +30,7 @@ import org.apache.iotdb.commons.client.sync.SyncConfigNodeIServiceClient; import org.apache.iotdb.commons.client.sync.SyncDataNodeDataBlockServiceClient; import org.apache.iotdb.commons.client.sync.SyncDataNodeInternalServiceClient; +import org.apache.iotdb.commons.consensus.PartitionRegionId; import org.apache.iotdb.db.conf.IoTDBConfig; import org.apache.iotdb.db.conf.IoTDBDescriptor; @@ -145,4 +146,21 @@ public KeyedObjectPool createCli .getConfig()); } } + + public static class ConfigNodeClientPoolFactory + implements IClientPoolFactory { + @Override + public KeyedObjectPool createClientPool( + ClientManager manager) { + return new GenericKeyedObjectPool<>( + new ConfigNodeClient.Factory( + manager, + new ClientFactoryProperty.Builder() + .setConnectionTimeoutMs(conf.getConnectionTimeoutInMS()) + .setRpcThriftCompressionEnabled(conf.isRpcThriftCompressionEnable()) + .setSelectorNumOfAsyncClientManager(conf.getSelectorNumOfClientManager()) + .build()), + new ClientPoolProperty.Builder().build().getConfig()); + } + } } diff --git a/server/src/main/java/org/apache/iotdb/db/conf/IoTDBStartCheck.java b/server/src/main/java/org/apache/iotdb/db/conf/IoTDBStartCheck.java index bd408eea38f9..533ce1858666 100644 --- a/server/src/main/java/org/apache/iotdb/db/conf/IoTDBStartCheck.java +++ b/server/src/main/java/org/apache/iotdb/db/conf/IoTDBStartCheck.java @@ -18,12 +18,9 @@ */ package org.apache.iotdb.db.conf; -import org.apache.iotdb.common.rpc.thrift.TEndPoint; import org.apache.iotdb.commons.conf.IoTDBConstant; -import org.apache.iotdb.commons.exception.BadNodeUrlException; import org.apache.iotdb.commons.exception.ConfigurationException; import org.apache.iotdb.commons.file.SystemFileFactory; -import org.apache.iotdb.commons.utils.NodeUrlUtils; import org.apache.iotdb.db.metadata.upgrade.MetadataUpgrader; import org.apache.iotdb.tsfile.common.conf.TSFileConfig; import org.apache.iotdb.tsfile.common.conf.TSFileDescriptor; @@ -41,7 +38,6 @@ import java.io.InputStreamReader; import java.nio.file.Files; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; @@ -415,38 +411,4 @@ public void serializeDataNodeId(int dataNodeId) throws IOException { // rename system.properties.tmp to system.properties FileUtils.moveFile(tmpPropertiesFile, propertiesFile); } - - /** call this method to serialize config node list */ - public void serializeConfigNodeList(List configNodeList) throws IOException { - // create an empty tmpPropertiesFile - if (tmpPropertiesFile.createNewFile()) { - logger.info("Create system.properties.tmp {}.", tmpPropertiesFile); - } else { - logger.error("Create system.properties.tmp {} failed.", tmpPropertiesFile); - System.exit(-1); - } - - try (FileOutputStream tmpFOS = new FileOutputStream(tmpPropertiesFile.toString())) { - properties.setProperty(CONFIG_NODE_LIST, NodeUrlUtils.convertTEndPointUrls(configNodeList)); - properties.store(tmpFOS, SYSTEM_PROPERTIES_STRING); - // serialize finished, delete old system.properties file - if (propertiesFile.exists()) { - Files.delete(propertiesFile.toPath()); - } - } - // rename system.properties.tmp to system.properties - FileUtils.moveFile(tmpPropertiesFile, propertiesFile); - } - - public void loadConfigNodeList() { - // properties contain CONFIG_NODE_LIST only when start as Data node - try { - if (properties.containsKey(CONFIG_NODE_LIST)) { - config.setConfigNodeList( - NodeUrlUtils.parseTEndPointUrls(properties.getProperty(CONFIG_NODE_LIST))); - } - } catch (BadNodeUrlException e) { - logger.error("Cannot parse config node list in system.properties"); - } - } } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/Coordinator.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/Coordinator.java index c6f6c4a2b1d6..d56457a7ce5a 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/Coordinator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/Coordinator.java @@ -22,6 +22,8 @@ import org.apache.iotdb.commons.client.IClientManager; import org.apache.iotdb.commons.client.sync.SyncDataNodeInternalServiceClient; import org.apache.iotdb.commons.concurrent.IoTDBThreadPoolFactory; +import org.apache.iotdb.commons.consensus.PartitionRegionId; +import org.apache.iotdb.db.client.ConfigNodeClient; import org.apache.iotdb.db.client.DataNodeClientPoolFactory; import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.mpp.common.MPPQueryContext; @@ -74,6 +76,10 @@ public class Coordinator { .createClientManager( new DataNodeClientPoolFactory.SyncDataNodeInternalServiceClientPoolFactory()); + private static final IClientManager + CONFIG_NODE_CLIENT_MANAGER = + new IClientManager.Factory() + .createClientManager(new DataNodeClientPoolFactory.ConfigNodeClientPoolFactory()); private final ExecutorService executor; private final ExecutorService writeOperationExecutor; private final ScheduledExecutorService scheduledExecutor; @@ -96,7 +102,7 @@ private IQueryExecution createQueryExecution( ISchemaFetcher schemaFetcher) { if (statement instanceof IConfigStatement) { queryContext.setQueryType(((IConfigStatement) statement).getQueryType()); - return new ConfigExecution(queryContext, statement, executor); + return new ConfigExecution(queryContext, statement, executor, CONFIG_NODE_CLIENT_MANAGER); } return new QueryExecution( statement, diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ClusterPartitionFetcher.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ClusterPartitionFetcher.java index 8a037223a6f7..366c4add9a9b 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ClusterPartitionFetcher.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ClusterPartitionFetcher.java @@ -21,6 +21,8 @@ import org.apache.iotdb.common.rpc.thrift.TRegionReplicaSet; import org.apache.iotdb.common.rpc.thrift.TSeriesPartitionSlot; import org.apache.iotdb.common.rpc.thrift.TTimePartitionSlot; +import org.apache.iotdb.commons.client.IClientManager; +import org.apache.iotdb.commons.consensus.PartitionRegionId; import org.apache.iotdb.commons.exception.MetadataException; import org.apache.iotdb.commons.partition.DataPartition; import org.apache.iotdb.commons.partition.DataPartitionQueryParam; @@ -35,17 +37,19 @@ import org.apache.iotdb.confignode.rpc.thrift.TStorageGroupSchema; import org.apache.iotdb.confignode.rpc.thrift.TStorageGroupSchemaResp; import org.apache.iotdb.db.client.ConfigNodeClient; +import org.apache.iotdb.db.client.ConfigNodeInfo; +import org.apache.iotdb.db.client.DataNodeClientPoolFactory; import org.apache.iotdb.db.conf.IoTDBConfig; import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.exception.sql.StatementAnalyzeException; import org.apache.iotdb.db.metadata.utils.MetaUtils; import org.apache.iotdb.db.mpp.common.schematree.PathPatternTree; -import org.apache.iotdb.rpc.IoTDBConnectionException; import org.apache.iotdb.rpc.TSStatusCode; import org.apache.iotdb.tsfile.utils.PublicBAOS; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; +import org.apache.thrift.TException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -66,12 +70,15 @@ public class ClusterPartitionFetcher implements IPartitionFetcher { private static final Logger logger = LoggerFactory.getLogger(ClusterPartitionFetcher.class); private static final IoTDBConfig config = IoTDBDescriptor.getInstance().getConfig(); private static final List ROOT_PATH = Arrays.asList("root", "**"); - private final ConfigNodeClient client; private final SeriesPartitionExecutor partitionExecutor; private PartitionCache partitionCache; + private final IClientManager configNodeClientManager = + new IClientManager.Factory() + .createClientManager(new DataNodeClientPoolFactory.ConfigNodeClientPoolFactory()); + private static final class ClusterPartitionFetcherHolder { private static final ClusterPartitionFetcher INSTANCE = new ClusterPartitionFetcher(); @@ -83,11 +90,6 @@ public static ClusterPartitionFetcher getInstance() { } private ClusterPartitionFetcher() { - try { - client = new ConfigNodeClient(); - } catch (IoTDBConnectionException e) { - throw new StatementAnalyzeException("Couldn't connect config node"); - } this.partitionExecutor = SeriesPartitionExecutor.getSeriesPartitionExecutor( config.getSeriesPartitionExecutorClass(), config.getSeriesPartitionSlotNum()); @@ -98,7 +100,8 @@ private ClusterPartitionFetcher() { @Override public SchemaPartition getSchemaPartition(PathPatternTree patternTree) { - try { + try (ConfigNodeClient client = + configNodeClientManager.borrowClient(ConfigNodeInfo.partitionRegionId)) { patternTree.constructTree(); List devicePaths = patternTree.findAllDevicePaths(); Map deviceToStorageGroupMap = getDeviceToStorageGroup(devicePaths, false); @@ -113,7 +116,7 @@ public SchemaPartition getSchemaPartition(PathPatternTree patternTree) { } } return schemaPartition; - } catch (IoTDBConnectionException e) { + } catch (TException | IOException e) { throw new StatementAnalyzeException( "An error occurred when executing getSchemaPartition():" + e.getMessage()); } @@ -121,7 +124,8 @@ public SchemaPartition getSchemaPartition(PathPatternTree patternTree) { @Override public SchemaPartition getOrCreateSchemaPartition(PathPatternTree patternTree) { - try { + try (ConfigNodeClient client = + configNodeClientManager.borrowClient(ConfigNodeInfo.partitionRegionId)) { patternTree.constructTree(); List devicePaths = patternTree.findAllDevicePaths(); Map deviceToStorageGroupMap = getDeviceToStorageGroup(devicePaths, true); @@ -136,7 +140,7 @@ public SchemaPartition getOrCreateSchemaPartition(PathPatternTree patternTree) { } } return schemaPartition; - } catch (IoTDBConnectionException e) { + } catch (TException | IOException e) { throw new StatementAnalyzeException( "An error occurred when executing getOrCreateSchemaPartition():" + e.getMessage()); } @@ -145,13 +149,14 @@ public SchemaPartition getOrCreateSchemaPartition(PathPatternTree patternTree) { @Override public DataPartition getDataPartition( Map> sgNameToQueryParamsMap) { - try { + try (ConfigNodeClient client = + configNodeClientManager.borrowClient(ConfigNodeInfo.partitionRegionId)) { TDataPartitionResp dataPartitionResp = client.getDataPartition(constructDataPartitionReq(sgNameToQueryParamsMap)); if (dataPartitionResp.getStatus().getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) { return parseDataPartitionResp(dataPartitionResp); } - } catch (IoTDBConnectionException e) { + } catch (TException | IOException e) { throw new StatementAnalyzeException( "An error occurred when executing getDataPartition():" + e.getMessage()); } @@ -160,7 +165,8 @@ public DataPartition getDataPartition( @Override public DataPartition getDataPartition(List dataPartitionQueryParams) { - try { + try (ConfigNodeClient client = + configNodeClientManager.borrowClient(ConfigNodeInfo.partitionRegionId)) { Map> splitDataPartitionQueryParams = splitDataPartitionQueryParam(dataPartitionQueryParams, false); DataPartition dataPartition = partitionCache.getDataPartition(splitDataPartitionQueryParams); @@ -174,7 +180,7 @@ public DataPartition getDataPartition(List dataPartitio } } return dataPartition; - } catch (IoTDBConnectionException e) { + } catch (TException | IOException e) { throw new StatementAnalyzeException( "An error occurred when executing getDataPartition():" + e.getMessage()); } @@ -184,13 +190,14 @@ public DataPartition getDataPartition(List dataPartitio public DataPartition getOrCreateDataPartition( Map> sgNameToQueryParamsMap) { // Do not use data partition cache - try { + try (ConfigNodeClient client = + configNodeClientManager.borrowClient(ConfigNodeInfo.partitionRegionId)) { TDataPartitionResp dataPartitionResp = client.getOrCreateDataPartition(constructDataPartitionReq(sgNameToQueryParamsMap)); if (dataPartitionResp.getStatus().getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) { return parseDataPartitionResp(dataPartitionResp); } - } catch (IoTDBConnectionException e) { + } catch (TException | IOException e) { throw new StatementAnalyzeException( "An error occurred when executing getOrCreateDataPartition():" + e.getMessage()); } @@ -200,7 +207,8 @@ public DataPartition getOrCreateDataPartition( @Override public DataPartition getOrCreateDataPartition( List dataPartitionQueryParams) { - try { + try (ConfigNodeClient client = + configNodeClientManager.borrowClient(ConfigNodeInfo.partitionRegionId)) { Map> splitDataPartitionQueryParams = splitDataPartitionQueryParam(dataPartitionQueryParams, true); DataPartition dataPartition = partitionCache.getDataPartition(splitDataPartitionQueryParams); @@ -215,7 +223,7 @@ public DataPartition getOrCreateDataPartition( } } return dataPartition; - } catch (IoTDBConnectionException e) { + } catch (TException | IOException e) { throw new StatementAnalyzeException( "An error occurred when executing getOrCreateDataPartition():" + e.getMessage()); } @@ -244,7 +252,8 @@ private Map getDeviceToStorageGroup( boolean firstTryResult = partitionCache.getStorageGroup(devicePaths, deviceToStorageGroup); if (!firstTryResult) { List storageGroupPathPattern = ROOT_PATH; - try { + try (ConfigNodeClient client = + configNodeClientManager.borrowClient(ConfigNodeInfo.partitionRegionId)) { TStorageGroupSchemaResp storageGroupSchemaResp = client.getMatchedStorageGroupSchemas(storageGroupPathPattern); if (storageGroupSchemaResp.getStatus().getCode() @@ -285,7 +294,7 @@ private Map getDeviceToStorageGroup( } } } - } catch (IoTDBConnectionException | MetadataException e) { + } catch (TException | MetadataException | IOException e) { throw new StatementAnalyzeException( "An error occurred when executing getOrCreateDataPartition():" + e.getMessage()); } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/AuthorizerConfigTask.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/AuthorizerConfigTask.java index e3d43e18a7fb..31be6557a28b 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/AuthorizerConfigTask.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/AuthorizerConfigTask.java @@ -21,16 +21,18 @@ import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.commons.auth.AuthException; +import org.apache.iotdb.commons.client.IClientManager; +import org.apache.iotdb.commons.consensus.PartitionRegionId; import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerReq; import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerResp; import org.apache.iotdb.db.client.ConfigNodeClient; +import org.apache.iotdb.db.client.ConfigNodeInfo; import org.apache.iotdb.db.mpp.common.header.ColumnHeader; import org.apache.iotdb.db.mpp.common.header.DatasetHeader; import org.apache.iotdb.db.mpp.plan.analyze.QueryType; import org.apache.iotdb.db.mpp.plan.statement.sys.AuthorStatement; import org.apache.iotdb.db.qp.logical.sys.AuthorOperator; import org.apache.iotdb.db.qp.physical.sys.AuthorPlan; -import org.apache.iotdb.rpc.IoTDBConnectionException; import org.apache.iotdb.rpc.StatementExecutionException; import org.apache.iotdb.rpc.TSStatusCode; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; @@ -39,9 +41,11 @@ import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.SettableFuture; +import org.apache.thrift.TException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -58,9 +62,11 @@ public AuthorizerConfigTask(AuthorStatement authorStatement) { } @Override - public ListenableFuture execute() { + public ListenableFuture execute( + IClientManager clientManager) { SettableFuture future = null; - try { + try (ConfigNodeClient configNodeClient = + clientManager.borrowClient(ConfigNodeInfo.partitionRegionId)) { // Construct request using statement TAuthorizerReq req = new TAuthorizerReq( @@ -76,24 +82,26 @@ public ListenableFuture execute() { // Send request to some API server if (authorStatement.getQueryType() == QueryType.WRITE) { - future = operatePermission(req); + future = operatePermission(req, configNodeClient); } else { - future = queryPermission(req); + future = queryPermission(req, configNodeClient); } } catch (AuthException e) { LOGGER.error("No such privilege {}.", authorStatement.getAuthorType()); future.setException(e); + } catch (IOException e) { + LOGGER.error("can't connect to all config nodes", e); + future.setException(e); } // If the action is executed successfully, return the Future. // If your operation is async, you can return the corresponding future directly. return future; } - private SettableFuture operatePermission(TAuthorizerReq authorizerReq) { + private SettableFuture operatePermission( + TAuthorizerReq authorizerReq, ConfigNodeClient configNodeClient) { SettableFuture future = SettableFuture.create(); - ConfigNodeClient configNodeClient = null; try { - configNodeClient = new ConfigNodeClient(); // Send request to some API server TSStatus tsStatus = configNodeClient.operatePermission(authorizerReq); // Get response or throw exception @@ -108,25 +116,20 @@ private SettableFuture operatePermission(TAuthorizerReq author } else { future.set(new ConfigTaskResult(TSStatusCode.SUCCESS_STATUS)); } - } catch (IoTDBConnectionException e) { + } catch (TException e) { LOGGER.error("Failed to connect to config node."); future.setException(e); - } finally { - if (configNodeClient != null) { - configNodeClient.close(); - } } // If the action is executed successfully, return the Future. // If your operation is async, you can return the corresponding future directly. return future; } - private SettableFuture queryPermission(TAuthorizerReq authorizerReq) { + private SettableFuture queryPermission( + TAuthorizerReq authorizerReq, ConfigNodeClient configNodeClient) { SettableFuture future = SettableFuture.create(); - ConfigNodeClient configNodeClient = null; TAuthorizerResp authorizerResp; try { - configNodeClient = new ConfigNodeClient(); // Send request to some API server authorizerResp = configNodeClient.queryPermission(authorizerReq); // Get response or throw exception @@ -168,7 +171,7 @@ private SettableFuture queryPermission(TAuthorizerReq authoriz future.set( new ConfigTaskResult(TSStatusCode.SUCCESS_STATUS, builder.build(), datasetHeader)); } - } catch (IoTDBConnectionException e) { + } catch (TException e) { LOGGER.error("Failed to connect to config node."); future.setException(e); } finally { diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/ConfigExecution.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/ConfigExecution.java index b481c42d1266..669ed49b8443 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/ConfigExecution.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/ConfigExecution.java @@ -19,7 +19,10 @@ package org.apache.iotdb.db.mpp.plan.execution.config; +import org.apache.iotdb.commons.client.IClientManager; +import org.apache.iotdb.commons.consensus.PartitionRegionId; import org.apache.iotdb.commons.utils.TestOnly; +import org.apache.iotdb.db.client.ConfigNodeClient; import org.apache.iotdb.db.mpp.common.MPPQueryContext; import org.apache.iotdb.db.mpp.common.header.DatasetHeader; import org.apache.iotdb.db.mpp.execution.QueryStateMachine; @@ -54,7 +57,13 @@ public class ConfigExecution implements IQueryExecution { private boolean resultSetConsumed; private final IConfigTask task; - public ConfigExecution(MPPQueryContext context, Statement statement, ExecutorService executor) { + private IClientManager clientManager; + + public ConfigExecution( + MPPQueryContext context, + Statement statement, + ExecutorService executor, + IClientManager clientManager) { this.context = context; this.statement = statement; this.executor = executor; @@ -62,6 +71,7 @@ public ConfigExecution(MPPQueryContext context, Statement statement, ExecutorSer this.taskFuture = SettableFuture.create(); this.task = statement.accept(new ConfigTaskVisitor(), new ConfigTaskVisitor.TaskContext()); this.resultSetConsumed = false; + this.clientManager = clientManager; } @TestOnly @@ -78,7 +88,7 @@ public ConfigExecution( @Override public void start() { try { - ListenableFuture future = task.execute(); + ListenableFuture future = task.execute(clientManager); Futures.addCallback( future, new FutureCallback() { diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/CountStorageGroupTask.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/CountStorageGroupTask.java index 5f08ae0cce61..5d8fa105a765 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/CountStorageGroupTask.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/CountStorageGroupTask.java @@ -19,26 +19,30 @@ package org.apache.iotdb.db.mpp.plan.execution.config; +import org.apache.iotdb.commons.client.IClientManager; import org.apache.iotdb.commons.conf.IoTDBConstant; +import org.apache.iotdb.commons.consensus.PartitionRegionId; import org.apache.iotdb.commons.exception.MetadataException; import org.apache.iotdb.confignode.rpc.thrift.TCountStorageGroupResp; import org.apache.iotdb.db.client.ConfigNodeClient; +import org.apache.iotdb.db.client.ConfigNodeInfo; import org.apache.iotdb.db.conf.IoTDBConfig; import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.localconfignode.LocalConfigNode; import org.apache.iotdb.db.mpp.common.header.ColumnHeader; import org.apache.iotdb.db.mpp.common.header.DatasetHeader; import org.apache.iotdb.db.mpp.plan.statement.metadata.CountStorageGroupStatement; -import org.apache.iotdb.rpc.IoTDBConnectionException; import org.apache.iotdb.rpc.TSStatusCode; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.read.common.block.TsBlockBuilder; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.SettableFuture; +import org.apache.thrift.TException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -55,18 +59,18 @@ public CountStorageGroupTask(CountStorageGroupStatement countStorageGroupStateme } @Override - public ListenableFuture execute() throws InterruptedException { + public ListenableFuture execute( + IClientManager clientManager) + throws InterruptedException { SettableFuture future = SettableFuture.create(); int storageGroupNum = 0; if (config.isClusterMode()) { List storageGroupPathPattern = Arrays.asList(countStorageGroupStatement.getPartialPath().getNodes()); - ConfigNodeClient client = null; - try { - client = new ConfigNodeClient(); + try (ConfigNodeClient client = clientManager.borrowClient(ConfigNodeInfo.partitionRegionId)) { TCountStorageGroupResp resp = client.countMatchedStorageGroups(storageGroupPathPattern); storageGroupNum = resp.getCount(); - } catch (IoTDBConnectionException e) { + } catch (TException | IOException e) { LOGGER.error("Failed to connect to config node."); future.setException(e); } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/DeleteStorageGroupTask.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/DeleteStorageGroupTask.java index adce38eab24e..dceda3cb9507 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/DeleteStorageGroupTask.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/DeleteStorageGroupTask.java @@ -20,24 +20,28 @@ package org.apache.iotdb.db.mpp.plan.execution.config; import org.apache.iotdb.common.rpc.thrift.TSStatus; +import org.apache.iotdb.commons.client.IClientManager; +import org.apache.iotdb.commons.consensus.PartitionRegionId; import org.apache.iotdb.commons.exception.IllegalPathException; import org.apache.iotdb.commons.exception.MetadataException; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.confignode.rpc.thrift.TDeleteStorageGroupsReq; import org.apache.iotdb.db.client.ConfigNodeClient; +import org.apache.iotdb.db.client.ConfigNodeInfo; import org.apache.iotdb.db.conf.IoTDBConfig; import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.localconfignode.LocalConfigNode; import org.apache.iotdb.db.mpp.plan.statement.metadata.DeleteStorageGroupStatement; -import org.apache.iotdb.rpc.IoTDBConnectionException; import org.apache.iotdb.rpc.StatementExecutionException; import org.apache.iotdb.rpc.TSStatusCode; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.SettableFuture; +import org.apache.thrift.TException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; import java.util.List; import java.util.stream.Collectors; @@ -53,15 +57,14 @@ public DeleteStorageGroupTask(DeleteStorageGroupStatement deleteStorageGroupStat } @Override - public ListenableFuture execute() { + public ListenableFuture execute( + IClientManager clientManager) { SettableFuture future = SettableFuture.create(); if (config.isClusterMode()) { TDeleteStorageGroupsReq req = new TDeleteStorageGroupsReq(deleteStorageGroupStatement.getPrefixPath()); - ConfigNodeClient configNodeClient = null; - try { - configNodeClient = new ConfigNodeClient(); - TSStatus tsStatus = configNodeClient.deleteStorageGroups(req); + try (ConfigNodeClient client = clientManager.borrowClient(ConfigNodeInfo.partitionRegionId)) { + TSStatus tsStatus = client.deleteStorageGroups(req); if (TSStatusCode.SUCCESS_STATUS.getStatusCode() != tsStatus.getCode()) { LOGGER.error( "Failed to execute delete storage group {} in config node, status is {}.", @@ -71,7 +74,7 @@ public ListenableFuture execute() { } else { future.set(new ConfigTaskResult(TSStatusCode.SUCCESS_STATUS)); } - } catch (IoTDBConnectionException e) { + } catch (TException | IOException e) { LOGGER.error("Failed to connect to config node."); future.setException(e); } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/IConfigTask.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/IConfigTask.java index 70013198474d..f7fbd4164ecb 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/IConfigTask.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/IConfigTask.java @@ -19,8 +19,14 @@ package org.apache.iotdb.db.mpp.plan.execution.config; +import org.apache.iotdb.commons.client.IClientManager; +import org.apache.iotdb.commons.consensus.PartitionRegionId; +import org.apache.iotdb.db.client.ConfigNodeClient; + import com.google.common.util.concurrent.ListenableFuture; public interface IConfigTask { - ListenableFuture execute() throws InterruptedException; + ListenableFuture execute( + IClientManager clientManager) + throws InterruptedException; } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/SetStorageGroupTask.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/SetStorageGroupTask.java index 03ce9dcbb10f..a06c54f7749c 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/SetStorageGroupTask.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/SetStorageGroupTask.java @@ -20,20 +20,23 @@ package org.apache.iotdb.db.mpp.plan.execution.config; import org.apache.iotdb.common.rpc.thrift.TSStatus; +import org.apache.iotdb.commons.client.IClientManager; +import org.apache.iotdb.commons.consensus.PartitionRegionId; import org.apache.iotdb.commons.exception.MetadataException; import org.apache.iotdb.confignode.rpc.thrift.TSetStorageGroupReq; import org.apache.iotdb.confignode.rpc.thrift.TStorageGroupSchema; import org.apache.iotdb.db.client.ConfigNodeClient; +import org.apache.iotdb.db.client.ConfigNodeInfo; import org.apache.iotdb.db.conf.IoTDBConfig; import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.localconfignode.LocalConfigNode; import org.apache.iotdb.db.mpp.plan.statement.metadata.SetStorageGroupStatement; -import org.apache.iotdb.rpc.IoTDBConnectionException; import org.apache.iotdb.rpc.StatementExecutionException; import org.apache.iotdb.rpc.TSStatusCode; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.SettableFuture; +import org.apache.thrift.TException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -51,16 +54,16 @@ public SetStorageGroupTask(SetStorageGroupStatement setStorageGroupStatement) { } @Override - public ListenableFuture execute() { + public ListenableFuture execute( + IClientManager clientManager) { SettableFuture future = SettableFuture.create(); // TODO:(this judgement needs to be integrated in a high level framework) if (config.isClusterMode()) { // Construct request using statement TStorageGroupSchema storageGroupSchema = constructStorageGroupSchema(); TSetStorageGroupReq req = new TSetStorageGroupReq(storageGroupSchema); - ConfigNodeClient configNodeClient = null; - try { - configNodeClient = new ConfigNodeClient(); + try (ConfigNodeClient configNodeClient = + clientManager.borrowClient(ConfigNodeInfo.partitionRegionId)) { // Send request to some API server TSStatus tsStatus = configNodeClient.setStorageGroup(req); // Get response or throw exception @@ -73,13 +76,9 @@ public ListenableFuture execute() { } else { future.set(new ConfigTaskResult(TSStatusCode.SUCCESS_STATUS)); } - } catch (IoTDBConnectionException e) { + } catch (TException | IOException e) { LOGGER.error("Failed to connect to config node."); future.setException(e); - } finally { - if (configNodeClient != null) { - configNodeClient.close(); - } } } else { try { diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/SetTTLTask.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/SetTTLTask.java index 0a3af1919155..241634f074a3 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/SetTTLTask.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/SetTTLTask.java @@ -20,19 +20,22 @@ package org.apache.iotdb.db.mpp.plan.execution.config; import org.apache.iotdb.common.rpc.thrift.TSStatus; +import org.apache.iotdb.commons.client.IClientManager; +import org.apache.iotdb.commons.consensus.PartitionRegionId; import org.apache.iotdb.commons.exception.MetadataException; import org.apache.iotdb.confignode.rpc.thrift.TSetTTLReq; import org.apache.iotdb.db.client.ConfigNodeClient; +import org.apache.iotdb.db.client.ConfigNodeInfo; import org.apache.iotdb.db.conf.IoTDBConfig; import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.localconfignode.LocalConfigNode; import org.apache.iotdb.db.mpp.plan.statement.metadata.SetTTLStatement; -import org.apache.iotdb.rpc.IoTDBConnectionException; import org.apache.iotdb.rpc.StatementExecutionException; import org.apache.iotdb.rpc.TSStatusCode; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.SettableFuture; +import org.apache.thrift.TException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -52,14 +55,15 @@ public SetTTLTask(SetTTLStatement statement) { } @Override - public ListenableFuture execute() throws InterruptedException { + public ListenableFuture execute( + IClientManager clientManager) + throws InterruptedException { SettableFuture future = SettableFuture.create(); if (config.isClusterMode()) { TSetTTLReq setTTLReq = new TSetTTLReq(statement.getStorageGroupPath().getFullPath(), statement.getTTL()); - ConfigNodeClient configNodeClient = null; - try { - configNodeClient = new ConfigNodeClient(); + try (ConfigNodeClient configNodeClient = + clientManager.borrowClient(ConfigNodeInfo.partitionRegionId)) { // Send request to some API server TSStatus tsStatus = configNodeClient.setTTL(setTTLReq); // Get response or throw exception @@ -73,13 +77,9 @@ public ListenableFuture execute() throws InterruptedException } else { future.set(new ConfigTaskResult(TSStatusCode.SUCCESS_STATUS)); } - } catch (IoTDBConnectionException e) { + } catch (TException | IOException e) { LOGGER.error("Failed to connect to config node."); future.setException(e); - } finally { - if (configNodeClient != null) { - configNodeClient.close(); - } } } else { try { diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/ShowStorageGroupTask.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/ShowStorageGroupTask.java index cd705d9cb15a..f066287187ef 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/ShowStorageGroupTask.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/ShowStorageGroupTask.java @@ -19,11 +19,14 @@ package org.apache.iotdb.db.mpp.plan.execution.config; +import org.apache.iotdb.commons.client.IClientManager; +import org.apache.iotdb.commons.consensus.PartitionRegionId; import org.apache.iotdb.commons.exception.MetadataException; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.confignode.rpc.thrift.TStorageGroupSchema; import org.apache.iotdb.confignode.rpc.thrift.TStorageGroupSchemaResp; import org.apache.iotdb.db.client.ConfigNodeClient; +import org.apache.iotdb.db.client.ConfigNodeInfo; import org.apache.iotdb.db.conf.IoTDBConfig; import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.localconfignode.LocalConfigNode; @@ -31,16 +34,17 @@ import org.apache.iotdb.db.mpp.common.header.DatasetHeader; import org.apache.iotdb.db.mpp.common.header.HeaderConstant; import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowStorageGroupStatement; -import org.apache.iotdb.rpc.IoTDBConnectionException; import org.apache.iotdb.rpc.TSStatusCode; import org.apache.iotdb.tsfile.read.common.block.TsBlockBuilder; import org.apache.iotdb.tsfile.utils.Binary; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.SettableFuture; +import org.apache.thrift.TException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -58,25 +62,21 @@ public ShowStorageGroupTask(ShowStorageGroupStatement showStorageGroupStatement) } @Override - public ListenableFuture execute() throws InterruptedException { + public ListenableFuture execute( + IClientManager clientManager) + throws InterruptedException { SettableFuture future = SettableFuture.create(); Map storageGroupSchemaMap = new HashMap<>(); if (config.isClusterMode()) { List storageGroupPathPattern = Arrays.asList(showStorageGroupStatement.getPathPattern().getNodes()); - ConfigNodeClient client = null; - try { - client = new ConfigNodeClient(); + try (ConfigNodeClient client = clientManager.borrowClient(ConfigNodeInfo.partitionRegionId)) { TStorageGroupSchemaResp resp = client.getMatchedStorageGroupSchemas(storageGroupPathPattern); storageGroupSchemaMap = resp.getStorageGroupSchemaMap(); - } catch (IoTDBConnectionException e) { + } catch (TException | IOException e) { LOGGER.error("Failed to connect to config node."); future.setException(e); - } finally { - if (client != null) { - client.close(); - } } } else { try { diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/ShowTTLTask.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/ShowTTLTask.java index 1cb7b0f978d0..f30978ebe1c7 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/ShowTTLTask.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/ShowTTLTask.java @@ -19,18 +19,20 @@ package org.apache.iotdb.db.mpp.plan.execution.config; +import org.apache.iotdb.commons.client.IClientManager; +import org.apache.iotdb.commons.consensus.PartitionRegionId; import org.apache.iotdb.commons.exception.MetadataException; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.confignode.rpc.thrift.TStorageGroupSchema; import org.apache.iotdb.confignode.rpc.thrift.TStorageGroupSchemaResp; import org.apache.iotdb.db.client.ConfigNodeClient; +import org.apache.iotdb.db.client.ConfigNodeInfo; import org.apache.iotdb.db.conf.IoTDBConfig; import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.localconfignode.LocalConfigNode; import org.apache.iotdb.db.mpp.common.header.DatasetHeader; import org.apache.iotdb.db.mpp.common.header.HeaderConstant; import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowTTLStatement; -import org.apache.iotdb.rpc.IoTDBConnectionException; import org.apache.iotdb.rpc.TSStatusCode; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.read.common.block.TsBlockBuilder; @@ -38,9 +40,11 @@ import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.SettableFuture; +import org.apache.thrift.TException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -58,14 +62,14 @@ public ShowTTLTask(ShowTTLStatement showTTLStatement) { } @Override - public ListenableFuture execute() throws InterruptedException { + public ListenableFuture execute( + IClientManager clientManager) + throws InterruptedException { SettableFuture future = SettableFuture.create(); List storageGroupPaths = showTTLStatement.getPaths(); Map storageGroupToTTL = new HashMap<>(); if (config.isClusterMode()) { - ConfigNodeClient client = null; - try { - client = new ConfigNodeClient(); + try (ConfigNodeClient client = clientManager.borrowClient(ConfigNodeInfo.partitionRegionId)) { if (showTTLStatement.isAll()) { List allStorageGroupPathPattern = Arrays.asList("root", "**"); TStorageGroupSchemaResp resp = @@ -87,13 +91,9 @@ public ListenableFuture execute() throws InterruptedException } } } - } catch (IoTDBConnectionException e) { + } catch (TException | IOException e) { LOGGER.error("Failed to connect to config node."); future.setException(e); - } finally { - if (client != null) { - client.close(); - } } } else { try { diff --git a/server/src/main/java/org/apache/iotdb/db/service/DataNode.java b/server/src/main/java/org/apache/iotdb/db/service/DataNode.java index 0edbf85f6fc1..8f51106bf953 100644 --- a/server/src/main/java/org/apache/iotdb/db/service/DataNode.java +++ b/server/src/main/java/org/apache/iotdb/db/service/DataNode.java @@ -32,6 +32,7 @@ import org.apache.iotdb.confignode.rpc.thrift.TDataNodeRegisterReq; import org.apache.iotdb.confignode.rpc.thrift.TDataNodeRegisterResp; import org.apache.iotdb.db.client.ConfigNodeClient; +import org.apache.iotdb.db.client.ConfigNodeInfo; import org.apache.iotdb.db.conf.IoTDBConfig; import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.conf.IoTDBStartCheck; @@ -58,9 +59,9 @@ import org.apache.iotdb.db.sync.receiver.ReceiverService; import org.apache.iotdb.db.sync.sender.service.SenderService; import org.apache.iotdb.db.wal.WALManager; -import org.apache.iotdb.rpc.IoTDBConnectionException; import org.apache.iotdb.rpc.TSStatusCode; +import org.apache.thrift.TException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -138,10 +139,11 @@ public boolean initLocalEngines() { public void joinCluster() throws StartupException { int retry = DEFAULT_JOIN_RETRY; + ConfigNodeInfo.getInstance() + .updateConfigNodeList(IoTDBDescriptor.getInstance().getConfig().getConfigNodeList()); while (retry > 0) { logger.info("start joining the cluster."); - try { - ConfigNodeClient configNodeClient = new ConfigNodeClient(); + try (ConfigNodeClient configNodeClient = new ConfigNodeClient()) { IoTDBConfig config = IoTDBDescriptor.getInstance().getConfig(); // Set DataNodeLocation @@ -170,8 +172,7 @@ public void joinCluster() throws StartupException { for (TConfigNodeLocation configNodeLocation : dataNodeRegisterResp.getConfigNodeList()) { configNodeList.add(configNodeLocation.getInternalEndPoint()); } - config.setConfigNodeList(configNodeList); - IoTDBStartCheck.getInstance().serializeConfigNodeList(configNodeList); + ConfigNodeInfo.getInstance().updateConfigNodeList(configNodeList); if (dataNodeRegisterResp.getStatus().getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode() @@ -189,10 +190,10 @@ public void joinCluster() throws StartupException { } } catch (IOException e) { logger.warn("Cannot join the cluster, because: {}", e.getMessage()); - } catch (IoTDBConnectionException e) { + } catch (TException e) { // read config nodes from system.properties logger.warn("Cannot join the cluster, because: {}", e.getMessage()); - IoTDBStartCheck.getInstance().loadConfigNodeList(); + ConfigNodeInfo.getInstance().loadConfigNodeList(); } try { diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/execution/ConfigExecutionTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/execution/ConfigExecutionTest.java index a71f3e41b097..0741a6b4fabf 100644 --- a/server/src/test/java/org/apache/iotdb/db/mpp/execution/ConfigExecutionTest.java +++ b/server/src/test/java/org/apache/iotdb/db/mpp/execution/ConfigExecutionTest.java @@ -19,7 +19,10 @@ package org.apache.iotdb.db.mpp.execution; +import org.apache.iotdb.commons.client.IClientManager; import org.apache.iotdb.commons.concurrent.IoTDBThreadPoolFactory; +import org.apache.iotdb.commons.consensus.PartitionRegionId; +import org.apache.iotdb.db.client.ConfigNodeClient; import org.apache.iotdb.db.mpp.common.MPPQueryContext; import org.apache.iotdb.db.mpp.common.QueryId; import org.apache.iotdb.db.mpp.common.header.ColumnHeader; @@ -51,7 +54,8 @@ public class ConfigExecutionTest { @Test public void normalConfigTaskTest() { - IConfigTask task = () -> immediateFuture(new ConfigTaskResult(TSStatusCode.SUCCESS_STATUS)); + IConfigTask task = + (clientManager) -> immediateFuture(new ConfigTaskResult(TSStatusCode.SUCCESS_STATUS)); ConfigExecution execution = new ConfigExecution(genMPPQueryContext(), null, getExecutor(), task); execution.start(); @@ -69,7 +73,7 @@ public void normalConfigTaskWithResultTest() { new DatasetHeader( Collections.singletonList(new ColumnHeader("TestValue", TSDataType.INT32)), false); IConfigTask task = - () -> + (clientManager) -> immediateFuture( new ConfigTaskResult(TSStatusCode.SUCCESS_STATUS, tsBlock, datasetHeader)); ConfigExecution execution = @@ -89,7 +93,7 @@ public void normalConfigTaskWithResultTest() { @Test public void exceptionConfigTaskTest() { IConfigTask task = - () -> { + (clientManager) -> { throw new RuntimeException("task throw exception when executing"); }; ConfigExecution execution = @@ -110,7 +114,9 @@ public SimpleTask(ListenableFuture future) { } @Override - public ListenableFuture execute() throws InterruptedException { + public ListenableFuture execute( + IClientManager clientManager) + throws InterruptedException { return result; } } @@ -133,7 +139,7 @@ public ListenableFuture execute() throws InterruptedException @Test public void exceptionAfterInvokeGetStatusTest() { IConfigTask task = - () -> { + (clientManager) -> { throw new RuntimeException("task throw exception when executing"); }; ConfigExecution execution = From 7a7e390680c512ed107d92ceb93ce26d2d550227 Mon Sep 17 00:00:00 2001 From: Xiangwei Wei <34242296+Alima777@users.noreply.github.com> Date: Tue, 17 May 2022 11:19:15 +0800 Subject: [PATCH 026/436] [IOTDB-2844] Implementaion of AggregationOperator and RawDataAggregationOperator (#5846) Co-authored-by: Jinrui.Zhang --- .../iotdb/db/mpp/aggregation/Accumulator.java | 3 +- .../iotdb/db/mpp/aggregation/Aggregator.java | 39 +- .../db/mpp/aggregation/AvgAccumulator.java | 38 +- .../db/mpp/aggregation/CountAccumulator.java | 8 +- .../mpp/aggregation/ExtremeAccumulator.java | 23 +- .../aggregation/FirstValueAccumulator.java | 25 +- .../FirstValueDescAccumulator.java | 12 +- .../mpp/aggregation/LastValueAccumulator.java | 32 +- .../aggregation/LastValueDescAccumulator.java | 48 +-- .../mpp/aggregation/MaxTimeAccumulator.java | 24 +- .../aggregation/MaxTimeDescAccumulator.java | 17 +- .../mpp/aggregation/MaxValueAccumulator.java | 22 +- .../mpp/aggregation/MinTimeAccumulator.java | 20 +- .../aggregation/MinTimeDescAccumulator.java | 2 +- .../mpp/aggregation/MinValueAccumulator.java | 22 +- .../db/mpp/aggregation/SumAccumulator.java | 34 +- .../timerangeiterator/AggrWindowIterator.java | 172 ++++++++ .../timerangeiterator/ITimeRangeIterator.java | 58 +++ .../PreAggrWindowIterator.java | 169 ++++++++ ...PreAggrWindowWithNaturalMonthIterator.java | 143 +++++++ .../SingleTimeWindowIterator.java | 7 +- .../TimeRangeIteratorFactory.java | 73 ++++ .../operator/process/AggregateOperator.java | 90 ++++- .../process/RawDataAggregateOperator.java | 194 +++++++++ .../source/SeriesAggregateScanOperator.java | 107 ++--- .../execution/schedule/DriverScheduler.java | 2 +- .../mpp/plan/planner/DistributionPlanner.java | 51 ++- .../plan/planner/LocalExecutionPlanner.java | 75 +++- .../plan/planner/plan/node/PlanVisitor.java | 2 +- .../plan/node/process/AggregationNode.java | 64 ++- .../plan/node/process/GroupByLevelNode.java | 6 +- .../source/SeriesAggregationScanNode.java | 12 +- .../plan/parameter/AggregationDescriptor.java | 61 ++- .../plan/parameter/AggregationStep.java | 44 +-- .../iotdb/db/query/expression/Expression.java | 2 +- .../db/mpp/aggregation/AccumulatorTest.java | 116 +++++- .../aggregation/TimeRangeIteratorTest.java | 298 ++++++++++++++ .../operator/AggregateOperatorTest.java | 321 +++++++++++++++ .../RawDataAggregateOperatorTest.java | 368 ++++++++++++++++++ .../SeriesAggregateScanOperatorTest.java | 25 +- .../process/AggregationNodeSerdeTest.java | 228 +++++++++++ .../iotdb/tsfile/read/common/TimeRange.java | 20 +- 42 files changed, 2799 insertions(+), 278 deletions(-) create mode 100644 server/src/main/java/org/apache/iotdb/db/mpp/aggregation/timerangeiterator/AggrWindowIterator.java create mode 100644 server/src/main/java/org/apache/iotdb/db/mpp/aggregation/timerangeiterator/ITimeRangeIterator.java create mode 100644 server/src/main/java/org/apache/iotdb/db/mpp/aggregation/timerangeiterator/PreAggrWindowIterator.java create mode 100644 server/src/main/java/org/apache/iotdb/db/mpp/aggregation/timerangeiterator/PreAggrWindowWithNaturalMonthIterator.java rename server/src/main/java/org/apache/iotdb/db/{utils => mpp/aggregation}/timerangeiterator/SingleTimeWindowIterator.java (92%) create mode 100644 server/src/main/java/org/apache/iotdb/db/mpp/aggregation/timerangeiterator/TimeRangeIteratorFactory.java create mode 100644 server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/RawDataAggregateOperator.java create mode 100644 server/src/test/java/org/apache/iotdb/db/mpp/aggregation/TimeRangeIteratorTest.java create mode 100644 server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/AggregateOperatorTest.java create mode 100644 server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/RawDataAggregateOperatorTest.java diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/Accumulator.java b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/Accumulator.java index 40c8a08d50f6..cfbdd7774e72 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/Accumulator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/Accumulator.java @@ -49,7 +49,8 @@ public interface Accumulator { /** * For aggregation function like COUNT, SUM, partialResult should be single, so its output column - * is single too; But for AVG, last_value, it should be double column with dictionary order. + * is single too; But for AVG(COUNT and SUM), LAST_VALUE(LAST_VALUE and MAX_TIME), the output + * columns should be double in dictionary order. */ void outputIntermediate(ColumnBuilder[] tsBlockBuilder); diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/Aggregator.java b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/Aggregator.java index a0a7b87f1ba8..6077648e5961 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/Aggregator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/Aggregator.java @@ -55,24 +55,38 @@ public Aggregator( this.inputLocationList = inputLocationList; } - // Used for SeriesAggregateScanOperator + // Used for SeriesAggregateScanOperator and RawDataAggregateOperator public void processTsBlock(TsBlock tsBlock) { checkArgument( - step.isInputRaw(), "Step in SeriesAggregateScanOperator can only process raw input"); + step.isInputRaw(), + "Step in SeriesAggregateScanOperator and RawDataAggregateOperator can only process raw input"); // TODO Aligned TimeSeries - accumulator.addInput(tsBlock.getTimeAndValueColumn(0), timeRange); + if (inputLocationList == null) { + accumulator.addInput(tsBlock.getTimeAndValueColumn(0), timeRange); + } else { + for (InputLocation[] inputLocations : inputLocationList) { + checkArgument( + inputLocations[0].getTsBlockIndex() == 0, + "RawDataAggregateOperator can only process one tsBlock input."); + Column[] timeValueColumn = new Column[2]; + timeValueColumn[0] = tsBlock.getTimeColumn(); + timeValueColumn[1] = tsBlock.getColumn(inputLocations[0].getValueColumnIndex()); + accumulator.addInput(timeValueColumn, timeRange); + } + } } - // Used for aggregateOperator + // Used for AggregateOperator public void processTsBlocks(TsBlock[] tsBlock) { - for (InputLocation[] inputLocations : inputLocationList) { - if (step.isInputRaw()) { - TsBlock rawTsBlock = tsBlock[inputLocations[0].getTsBlockIndex()]; - Column[] timeValueColumn = new Column[2]; - timeValueColumn[0] = rawTsBlock.getTimeColumn(); - timeValueColumn[1] = rawTsBlock.getColumn(inputLocations[0].getValueColumnIndex()); - accumulator.addInput(timeValueColumn, timeRange); - } else { + checkArgument(!step.isInputRaw(), "Step in AggregateOperator cannot process raw input"); + if (step.isInputFinal()) { + checkArgument(inputLocationList.size() == 1, "Final output can only be single column"); + Column finalResult = + tsBlock[inputLocationList.get(0)[0].getTsBlockIndex()].getColumn( + inputLocationList.get(0)[0].getValueColumnIndex()); + accumulator.setFinal(finalResult); + } else { + for (InputLocation[] inputLocations : inputLocationList) { Column[] columns = new Column[inputLocations.length]; for (int i = 0; i < inputLocations.length; i++) { columns[i] = @@ -105,6 +119,7 @@ public TSDataType[] getOutputType() { } public void reset() { + timeRange = new TimeRange(0, Long.MAX_VALUE); accumulator.reset(); } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/AvgAccumulator.java b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/AvgAccumulator.java index 3c726f9603d3..09dcf1462835 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/AvgAccumulator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/AvgAccumulator.java @@ -35,6 +35,7 @@ public class AvgAccumulator implements Accumulator { private TSDataType seriesDataType; private long countValue; private double sumValue; + private boolean initResult = false; public AvgAccumulator(TSDataType seriesDataType) { this.seriesDataType = seriesDataType; @@ -67,12 +68,17 @@ public void addInput(Column[] column, TimeRange timeRange) { @Override public void addIntermediate(Column[] partialResult) { checkArgument(partialResult.length == 2, "partialResult of Avg should be 2"); + if (partialResult[0].isNull(0)) { + return; + } + initResult = true; countValue += partialResult[0].getLong(0); sumValue += partialResult[1].getDouble(0); } @Override public void addStatistics(Statistics statistics) { + initResult = true; countValue += statistics.getCount(); if (statistics instanceof IntegerStatistics) { sumValue += statistics.getSumLongValue(); @@ -85,6 +91,10 @@ public void addStatistics(Statistics statistics) { @Override public void setFinal(Column finalResult) { reset(); + if (finalResult.isNull(0)) { + return; + } + initResult = true; countValue = 1; sumValue = finalResult.getDouble(0); } @@ -92,17 +102,27 @@ public void setFinal(Column finalResult) { @Override public void outputIntermediate(ColumnBuilder[] columnBuilders) { checkArgument(columnBuilders.length == 2, "partialResult of Avg should be 2"); - columnBuilders[0].writeLong(countValue); - columnBuilders[1].writeDouble(sumValue); + if (!initResult) { + columnBuilders[0].appendNull(); + columnBuilders[1].appendNull(); + } else { + columnBuilders[0].writeLong(countValue); + columnBuilders[1].writeDouble(sumValue); + } } @Override public void outputFinal(ColumnBuilder columnBuilder) { - columnBuilder.writeDouble(sumValue / countValue); + if (!initResult) { + columnBuilder.appendNull(); + } else { + columnBuilder.writeDouble(sumValue / countValue); + } } @Override public void reset() { + initResult = false; this.countValue = 0; this.sumValue = 0.0; } @@ -126,10 +146,11 @@ private void addIntInput(Column[] column, TimeRange timeRange) { TimeColumn timeColumn = (TimeColumn) column[0]; for (int i = 0; i < timeColumn.getPositionCount(); i++) { long curTime = timeColumn.getLong(i); - if (curTime >= timeRange.getMax() || curTime < timeRange.getMin()) { + if (curTime > timeRange.getMax() || curTime < timeRange.getMin()) { break; } if (!column[1].isNull(i)) { + initResult = true; countValue++; sumValue += column[1].getInt(i); } @@ -140,10 +161,11 @@ private void addLongInput(Column[] column, TimeRange timeRange) { TimeColumn timeColumn = (TimeColumn) column[0]; for (int i = 0; i < timeColumn.getPositionCount(); i++) { long curTime = timeColumn.getLong(i); - if (curTime >= timeRange.getMax() || curTime < timeRange.getMin()) { + if (curTime > timeRange.getMax() || curTime < timeRange.getMin()) { break; } if (!column[1].isNull(i)) { + initResult = true; countValue++; sumValue += column[1].getLong(i); } @@ -154,10 +176,11 @@ private void addFloatInput(Column[] column, TimeRange timeRange) { TimeColumn timeColumn = (TimeColumn) column[0]; for (int i = 0; i < timeColumn.getPositionCount(); i++) { long curTime = timeColumn.getLong(i); - if (curTime >= timeRange.getMax() || curTime < timeRange.getMin()) { + if (curTime > timeRange.getMax() || curTime < timeRange.getMin()) { break; } if (!column[1].isNull(i)) { + initResult = true; countValue++; sumValue += column[1].getFloat(i); } @@ -168,10 +191,11 @@ private void addDoubleInput(Column[] column, TimeRange timeRange) { TimeColumn timeColumn = (TimeColumn) column[0]; for (int i = 0; i < timeColumn.getPositionCount(); i++) { long curTime = timeColumn.getLong(i); - if (curTime >= timeRange.getMax() || curTime < timeRange.getMin()) { + if (curTime > timeRange.getMax() || curTime < timeRange.getMin()) { break; } if (!column[1].isNull(i)) { + initResult = true; countValue++; sumValue += column[1].getDouble(i); } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/CountAccumulator.java b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/CountAccumulator.java index c2825b00bb0a..eeae6a03776f 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/CountAccumulator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/CountAccumulator.java @@ -40,7 +40,7 @@ public void addInput(Column[] column, TimeRange timeRange) { TimeColumn timeColumn = (TimeColumn) column[0]; for (int i = 0; i < timeColumn.getPositionCount(); i++) { long curTime = timeColumn.getLong(i); - if (curTime >= timeRange.getMax() || curTime < timeRange.getMin()) { + if (curTime > timeRange.getMax() || curTime < timeRange.getMin()) { break; } if (!column[1].isNull(i)) { @@ -53,6 +53,9 @@ public void addInput(Column[] column, TimeRange timeRange) { @Override public void addIntermediate(Column[] partialResult) { checkArgument(partialResult.length == 1, "partialResult of Count should be 1"); + if (partialResult[0].isNull(0)) { + return; + } countValue += partialResult[0].getLong(0); } @@ -64,6 +67,9 @@ public void addStatistics(Statistics statistics) { // finalResult should be single column, like: | finalCountValue | @Override public void setFinal(Column finalResult) { + if (finalResult.isNull(0)) { + return; + } countValue = finalResult.getLong(0); } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/ExtremeAccumulator.java b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/ExtremeAccumulator.java index e70a7f484a9f..7dc57882434a 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/ExtremeAccumulator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/ExtremeAccumulator.java @@ -68,6 +68,9 @@ public void addInput(Column[] column, TimeRange timeRange) { @Override public void addIntermediate(Column[] partialResult) { checkArgument(partialResult.length == 1, "partialResult of ExtremeValue should be 1"); + if (partialResult[0].isNull(0)) { + return; + } switch (seriesDataType) { case INT32: updateIntResult(partialResult[0].getInt(0)); @@ -118,6 +121,10 @@ public void addStatistics(Statistics statistics) { @Override public void setFinal(Column finalResult) { + if (finalResult.isNull(0)) { + return; + } + initResult = true; extremeResult.setObject(finalResult.getObject(0)); } @@ -125,6 +132,10 @@ public void setFinal(Column finalResult) { @Override public void outputIntermediate(ColumnBuilder[] columnBuilders) { checkArgument(columnBuilders.length == 1, "partialResult of ExtremeValue should be 1"); + if (!initResult) { + columnBuilders[0].appendNull(); + return; + } switch (seriesDataType) { case INT32: columnBuilders[0].writeInt(extremeResult.getInt()); @@ -148,6 +159,10 @@ public void outputIntermediate(ColumnBuilder[] columnBuilders) { @Override public void outputFinal(ColumnBuilder columnBuilder) { + if (!initResult) { + columnBuilder.appendNull(); + return; + } switch (seriesDataType) { case INT32: columnBuilder.writeInt(extremeResult.getInt()); @@ -194,7 +209,7 @@ private void addIntInput(Column[] column, TimeRange timeRange) { TimeColumn timeColumn = (TimeColumn) column[0]; for (int i = 0; i < timeColumn.getPositionCount(); i++) { long curTime = timeColumn.getLong(i); - if (curTime >= timeRange.getMax() || curTime < timeRange.getMin()) { + if (curTime > timeRange.getMax() || curTime < timeRange.getMin()) { break; } if (!column[1].isNull(i)) { @@ -220,7 +235,7 @@ private void addLongInput(Column[] column, TimeRange timeRange) { TimeColumn timeColumn = (TimeColumn) column[0]; for (int i = 0; i < timeColumn.getPositionCount(); i++) { long curTime = timeColumn.getLong(i); - if (curTime >= timeRange.getMax() || curTime < timeRange.getMin()) { + if (curTime > timeRange.getMax() || curTime < timeRange.getMin()) { break; } if (!column[1].isNull(i)) { @@ -246,7 +261,7 @@ private void addFloatInput(Column[] column, TimeRange timeRange) { TimeColumn timeColumn = (TimeColumn) column[0]; for (int i = 0; i < timeColumn.getPositionCount(); i++) { long curTime = timeColumn.getLong(i); - if (curTime >= timeRange.getMax() || curTime < timeRange.getMin()) { + if (curTime > timeRange.getMax() || curTime < timeRange.getMin()) { break; } if (!column[1].isNull(i)) { @@ -272,7 +287,7 @@ private void addDoubleInput(Column[] column, TimeRange timeRange) { TimeColumn timeColumn = (TimeColumn) column[0]; for (int i = 0; i < timeColumn.getPositionCount(); i++) { long curTime = timeColumn.getLong(i); - if (curTime >= timeRange.getMax() || curTime < timeRange.getMin()) { + if (curTime > timeRange.getMax() || curTime < timeRange.getMin()) { break; } if (!column[1].isNull(i)) { diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/FirstValueAccumulator.java b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/FirstValueAccumulator.java index 45bce52036c7..ac0f4a423fe4 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/FirstValueAccumulator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/FirstValueAccumulator.java @@ -73,6 +73,9 @@ public void addInput(Column[] column, TimeRange timeRange) { @Override public void addIntermediate(Column[] partialResult) { checkArgument(partialResult.length == 2, "partialResult of FirstValue should be 2"); + if (partialResult[0].isNull(0)) { + return; + } switch (seriesDataType) { case INT32: updateIntFirstValue(partialResult[0].getInt(0), partialResult[1].getLong(0)); @@ -127,6 +130,7 @@ public void addStatistics(Statistics statistics) { @Override public void setFinal(Column finalResult) { reset(); + hasCandidateResult = true; firstValue.setObject(finalResult.getObject(0)); } @@ -134,6 +138,11 @@ public void setFinal(Column finalResult) { @Override public void outputIntermediate(ColumnBuilder[] columnBuilders) { checkArgument(columnBuilders.length == 2, "partialResult of FirstValue should be 2"); + if (!hasCandidateResult) { + columnBuilders[0].appendNull(); + columnBuilders[1].appendNull(); + return; + } switch (seriesDataType) { case INT32: columnBuilders[0].writeInt(firstValue.getInt()); @@ -162,6 +171,10 @@ public void outputIntermediate(ColumnBuilder[] columnBuilders) { @Override public void outputFinal(ColumnBuilder columnBuilder) { + if (!hasCandidateResult) { + columnBuilder.appendNull(); + return; + } switch (seriesDataType) { case INT32: columnBuilder.writeInt(firstValue.getInt()); @@ -212,7 +225,7 @@ public TSDataType getFinalType() { protected void addIntInput(Column[] column, TimeRange timeRange) { for (int i = 0; i < column[0].getPositionCount(); i++) { long curTime = column[0].getLong(i); - if (curTime >= timeRange.getMin() && curTime < timeRange.getMax() && !column[1].isNull(i)) { + if (timeRange.contains(curTime) && !column[1].isNull(i)) { updateIntFirstValue(column[1].getInt(i), curTime); break; } @@ -230,7 +243,7 @@ protected void updateIntFirstValue(int value, long curTime) { protected void addLongInput(Column[] column, TimeRange timeRange) { for (int i = 0; i < column[0].getPositionCount(); i++) { long curTime = column[0].getLong(i); - if (curTime >= timeRange.getMin() && curTime < timeRange.getMax() && !column[1].isNull(i)) { + if (timeRange.contains(curTime) && !column[1].isNull(i)) { updateLongFirstValue(column[1].getLong(i), curTime); break; } @@ -248,7 +261,7 @@ protected void updateLongFirstValue(long value, long curTime) { protected void addFloatInput(Column[] column, TimeRange timeRange) { for (int i = 0; i < column[0].getPositionCount(); i++) { long curTime = column[0].getLong(i); - if (curTime >= timeRange.getMin() && curTime < timeRange.getMax() && !column[1].isNull(i)) { + if (timeRange.contains(curTime) && !column[1].isNull(i)) { updateFloatFirstValue(column[1].getFloat(i), curTime); break; } @@ -266,7 +279,7 @@ protected void updateFloatFirstValue(float value, long curTime) { protected void addDoubleInput(Column[] column, TimeRange timeRange) { for (int i = 0; i < column[0].getPositionCount(); i++) { long curTime = column[0].getLong(i); - if (curTime >= timeRange.getMin() && curTime < timeRange.getMax() && !column[1].isNull(i)) { + if (timeRange.contains(curTime) && !column[1].isNull(i)) { updateDoubleFirstValue(column[1].getDouble(i), curTime); break; } @@ -284,7 +297,7 @@ protected void updateDoubleFirstValue(double value, long curTime) { protected void addBooleanInput(Column[] column, TimeRange timeRange) { for (int i = 0; i < column[0].getPositionCount(); i++) { long curTime = column[0].getLong(i); - if (curTime >= timeRange.getMin() && curTime < timeRange.getMax() && !column[1].isNull(i)) { + if (timeRange.contains(curTime) && !column[1].isNull(i)) { updateBooleanFirstValue(column[1].getBoolean(i), curTime); break; } @@ -302,7 +315,7 @@ protected void updateBooleanFirstValue(boolean value, long curTime) { protected void addBinaryInput(Column[] column, TimeRange timeRange) { for (int i = 0; i < column[0].getPositionCount(); i++) { long curTime = column[0].getLong(i); - if (curTime >= timeRange.getMin() && curTime < timeRange.getMax() && !column[1].isNull(i)) { + if (timeRange.contains(curTime) && !column[1].isNull(i)) { updateBinaryFirstValue(column[1].getBinary(i), curTime); break; } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/FirstValueDescAccumulator.java b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/FirstValueDescAccumulator.java index 08c04b818560..595e3277c634 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/FirstValueDescAccumulator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/FirstValueDescAccumulator.java @@ -38,7 +38,7 @@ public boolean hasFinalResult() { protected void addIntInput(Column[] column, TimeRange timeRange) { for (int i = 0; i < column[0].getPositionCount(); i++) { long curTime = column[0].getLong(i); - if (curTime >= timeRange.getMin() && curTime < timeRange.getMax() && !column[1].isNull(i)) { + if (timeRange.contains(curTime) && !column[1].isNull(i)) { updateIntFirstValue(column[1].getInt(i), curTime); } } @@ -47,7 +47,7 @@ protected void addIntInput(Column[] column, TimeRange timeRange) { protected void addLongInput(Column[] column, TimeRange timeRange) { for (int i = 0; i < column[0].getPositionCount(); i++) { long curTime = column[0].getLong(i); - if (curTime >= timeRange.getMin() && curTime < timeRange.getMax() && !column[1].isNull(i)) { + if (timeRange.contains(curTime) && !column[1].isNull(i)) { updateLongFirstValue(column[1].getLong(i), curTime); } } @@ -56,7 +56,7 @@ protected void addLongInput(Column[] column, TimeRange timeRange) { protected void addFloatInput(Column[] column, TimeRange timeRange) { for (int i = 0; i < column[0].getPositionCount(); i++) { long curTime = column[0].getLong(i); - if (curTime >= timeRange.getMin() && curTime < timeRange.getMax() && !column[1].isNull(i)) { + if (timeRange.contains(curTime) && !column[1].isNull(i)) { updateFloatFirstValue(column[1].getFloat(i), curTime); } } @@ -65,7 +65,7 @@ protected void addFloatInput(Column[] column, TimeRange timeRange) { protected void addDoubleInput(Column[] column, TimeRange timeRange) { for (int i = 0; i < column[0].getPositionCount(); i++) { long curTime = column[0].getLong(i); - if (curTime >= timeRange.getMin() && curTime < timeRange.getMax() && !column[1].isNull(i)) { + if (timeRange.contains(curTime) && !column[1].isNull(i)) { updateDoubleFirstValue(column[1].getDouble(i), curTime); } } @@ -74,7 +74,7 @@ protected void addDoubleInput(Column[] column, TimeRange timeRange) { protected void addBooleanInput(Column[] column, TimeRange timeRange) { for (int i = 0; i < column[0].getPositionCount(); i++) { long curTime = column[0].getLong(i); - if (curTime >= timeRange.getMin() && curTime < timeRange.getMax() && !column[1].isNull(i)) { + if (timeRange.contains(curTime) && !column[1].isNull(i)) { updateBooleanFirstValue(column[1].getBoolean(i), curTime); } } @@ -83,7 +83,7 @@ protected void addBooleanInput(Column[] column, TimeRange timeRange) { protected void addBinaryInput(Column[] column, TimeRange timeRange) { for (int i = 0; i < column[0].getPositionCount(); i++) { long curTime = column[0].getLong(i); - if (curTime >= timeRange.getMin() && curTime < timeRange.getMax() && !column[1].isNull(i)) { + if (timeRange.contains(curTime) && !column[1].isNull(i)) { updateBinaryFirstValue(column[1].getBinary(i), curTime); } } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/LastValueAccumulator.java b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/LastValueAccumulator.java index db418a490ee6..b884af9d5621 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/LastValueAccumulator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/LastValueAccumulator.java @@ -35,6 +35,7 @@ public class LastValueAccumulator implements Accumulator { protected final TSDataType seriesDataType; protected TsPrimitiveType lastValue; protected long maxTime = Long.MIN_VALUE; + protected boolean initResult = false; public LastValueAccumulator(TSDataType seriesDataType) { this.seriesDataType = seriesDataType; @@ -72,6 +73,9 @@ public void addInput(Column[] column, TimeRange timeRange) { @Override public void addIntermediate(Column[] partialResult) { checkArgument(partialResult.length == 2, "partialResult of LastValue should be 2"); + if (partialResult[0].isNull(0)) { + return; + } switch (seriesDataType) { case INT32: updateIntLastValue(partialResult[0].getInt(0), partialResult[1].getLong(0)); @@ -133,6 +137,11 @@ public void setFinal(Column finalResult) { @Override public void outputIntermediate(ColumnBuilder[] columnBuilders) { checkArgument(columnBuilders.length == 2, "partialResult of LastValue should be 2"); + if (!initResult) { + columnBuilders[0].appendNull(); + columnBuilders[1].appendNull(); + return; + } switch (seriesDataType) { case INT32: columnBuilders[0].writeInt(lastValue.getInt()); @@ -161,6 +170,10 @@ public void outputIntermediate(ColumnBuilder[] columnBuilders) { @Override public void outputFinal(ColumnBuilder columnBuilder) { + if (!initResult) { + columnBuilder.appendNull(); + return; + } switch (seriesDataType) { case INT32: columnBuilder.writeInt(lastValue.getInt()); @@ -188,6 +201,7 @@ public void outputFinal(ColumnBuilder columnBuilder) { @Override public void reset() { + initResult = false; this.maxTime = Long.MIN_VALUE; this.lastValue.reset(); } @@ -210,13 +224,14 @@ public TSDataType getFinalType() { protected void addIntInput(Column[] column, TimeRange timeRange) { for (int i = 0; i < column[0].getPositionCount(); i++) { long curTime = column[0].getLong(i); - if (curTime >= timeRange.getMin() && curTime < timeRange.getMax() && !column[1].isNull(i)) { + if (timeRange.contains(curTime) && !column[1].isNull(i)) { updateIntLastValue(column[1].getInt(i), curTime); } } } protected void updateIntLastValue(int value, long curTime) { + initResult = true; if (curTime > maxTime) { maxTime = curTime; lastValue.setInt(value); @@ -226,13 +241,14 @@ protected void updateIntLastValue(int value, long curTime) { protected void addLongInput(Column[] column, TimeRange timeRange) { for (int i = 0; i < column[0].getPositionCount(); i++) { long curTime = column[0].getLong(i); - if (curTime >= timeRange.getMin() && curTime < timeRange.getMax() && !column[1].isNull(i)) { + if (timeRange.contains(curTime) && !column[1].isNull(i)) { updateLongLastValue(column[1].getLong(i), curTime); } } } protected void updateLongLastValue(long value, long curTime) { + initResult = true; if (curTime > maxTime) { maxTime = curTime; lastValue.setLong(value); @@ -242,13 +258,14 @@ protected void updateLongLastValue(long value, long curTime) { protected void addFloatInput(Column[] column, TimeRange timeRange) { for (int i = 0; i < column[0].getPositionCount(); i++) { long curTime = column[0].getLong(i); - if (curTime >= timeRange.getMin() && curTime < timeRange.getMax() && !column[1].isNull(i)) { + if (timeRange.contains(curTime) && !column[1].isNull(i)) { updateFloatLastValue(column[1].getFloat(i), curTime); } } } protected void updateFloatLastValue(float value, long curTime) { + initResult = true; if (curTime > maxTime) { maxTime = curTime; lastValue.setFloat(value); @@ -258,13 +275,14 @@ protected void updateFloatLastValue(float value, long curTime) { protected void addDoubleInput(Column[] column, TimeRange timeRange) { for (int i = 0; i < column[0].getPositionCount(); i++) { long curTime = column[0].getLong(i); - if (curTime >= timeRange.getMin() && curTime < timeRange.getMax() && !column[1].isNull(i)) { + if (timeRange.contains(curTime) && !column[1].isNull(i)) { updateDoubleLastValue(column[1].getDouble(i), curTime); } } } protected void updateDoubleLastValue(double value, long curTime) { + initResult = true; if (curTime > maxTime) { maxTime = curTime; lastValue.setDouble(value); @@ -274,13 +292,14 @@ protected void updateDoubleLastValue(double value, long curTime) { protected void addBooleanInput(Column[] column, TimeRange timeRange) { for (int i = 0; i < column[0].getPositionCount(); i++) { long curTime = column[0].getLong(i); - if (curTime >= timeRange.getMin() && curTime < timeRange.getMax() && !column[1].isNull(i)) { + if (timeRange.contains(curTime) && !column[1].isNull(i)) { updateBooleanLastValue(column[1].getBoolean(i), curTime); } } } protected void updateBooleanLastValue(boolean value, long curTime) { + initResult = true; if (curTime > maxTime) { maxTime = curTime; lastValue.setBoolean(value); @@ -290,13 +309,14 @@ protected void updateBooleanLastValue(boolean value, long curTime) { protected void addBinaryInput(Column[] column, TimeRange timeRange) { for (int i = 0; i < column[0].getPositionCount(); i++) { long curTime = column[0].getLong(i); - if (curTime >= timeRange.getMin() && curTime < timeRange.getMax() && !column[1].isNull(i)) { + if (timeRange.contains(curTime) && !column[1].isNull(i)) { updateBinaryLastValue(column[1].getBinary(i), curTime); } } } protected void updateBinaryLastValue(Binary value, long curTime) { + initResult = true; if (curTime > maxTime) { maxTime = curTime; lastValue.setBinary(value); diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/LastValueDescAccumulator.java b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/LastValueDescAccumulator.java index 3360bad7a3b7..0339a2b10200 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/LastValueDescAccumulator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/LastValueDescAccumulator.java @@ -22,31 +22,27 @@ import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.read.common.TimeRange; import org.apache.iotdb.tsfile.read.common.block.column.Column; -import org.apache.iotdb.tsfile.utils.Binary; public class LastValueDescAccumulator extends LastValueAccumulator { - private boolean hasCandidateResult = false; - public LastValueDescAccumulator(TSDataType seriesDataType) { super(seriesDataType); } @Override public boolean hasFinalResult() { - return hasCandidateResult; + return initResult; } @Override public void reset() { - hasCandidateResult = false; super.reset(); } protected void addIntInput(Column[] column, TimeRange timeRange) { for (int i = 0; i < column[0].getPositionCount(); i++) { long curTime = column[0].getLong(i); - if (curTime >= timeRange.getMin() && curTime < timeRange.getMax() && !column[1].isNull(i)) { + if (timeRange.contains(curTime) && !column[1].isNull(i)) { updateIntLastValue(column[1].getInt(i), curTime); break; } @@ -56,7 +52,7 @@ protected void addIntInput(Column[] column, TimeRange timeRange) { protected void addLongInput(Column[] column, TimeRange timeRange) { for (int i = 0; i < column[0].getPositionCount(); i++) { long curTime = column[0].getLong(i); - if (curTime >= timeRange.getMin() && curTime < timeRange.getMax() && !column[1].isNull(i)) { + if (timeRange.contains(curTime) && !column[1].isNull(i)) { updateLongLastValue(column[1].getLong(i), curTime); break; } @@ -66,7 +62,7 @@ protected void addLongInput(Column[] column, TimeRange timeRange) { protected void addFloatInput(Column[] column, TimeRange timeRange) { for (int i = 0; i < column[0].getPositionCount(); i++) { long curTime = column[0].getLong(i); - if (curTime >= timeRange.getMin() && curTime < timeRange.getMax() && !column[1].isNull(i)) { + if (timeRange.contains(curTime) && !column[1].isNull(i)) { updateFloatLastValue(column[1].getFloat(i), curTime); break; } @@ -76,7 +72,7 @@ protected void addFloatInput(Column[] column, TimeRange timeRange) { protected void addDoubleInput(Column[] column, TimeRange timeRange) { for (int i = 0; i < column[0].getPositionCount(); i++) { long curTime = column[0].getLong(i); - if (curTime >= timeRange.getMin() && curTime < timeRange.getMax() && !column[1].isNull(i)) { + if (timeRange.contains(curTime) && !column[1].isNull(i)) { updateDoubleLastValue(column[1].getDouble(i), curTime); break; } @@ -86,7 +82,7 @@ protected void addDoubleInput(Column[] column, TimeRange timeRange) { protected void addBooleanInput(Column[] column, TimeRange timeRange) { for (int i = 0; i < column[0].getPositionCount(); i++) { long curTime = column[0].getLong(i); - if (curTime >= timeRange.getMin() && curTime < timeRange.getMax() && !column[1].isNull(i)) { + if (timeRange.contains(curTime) && !column[1].isNull(i)) { updateBooleanLastValue(column[1].getBoolean(i), curTime); break; } @@ -96,40 +92,10 @@ protected void addBooleanInput(Column[] column, TimeRange timeRange) { protected void addBinaryInput(Column[] column, TimeRange timeRange) { for (int i = 0; i < column[0].getPositionCount(); i++) { long curTime = column[0].getLong(i); - if (curTime >= timeRange.getMin() && curTime < timeRange.getMax() && !column[1].isNull(i)) { + if (timeRange.contains(curTime) && !column[1].isNull(i)) { updateBinaryLastValue(column[1].getBinary(i), curTime); break; } } } - - protected void updateIntLastValue(int value, long curTime) { - hasCandidateResult = true; - super.updateIntLastValue(value, curTime); - } - - protected void updateLongLastValue(long value, long curTime) { - hasCandidateResult = true; - super.updateLongLastValue(value, curTime); - } - - protected void updateFloatLastValue(float value, long curTime) { - hasCandidateResult = true; - super.updateFloatLastValue(value, curTime); - } - - protected void updateDoubleLastValue(double value, long curTime) { - hasCandidateResult = true; - super.updateDoubleLastValue(value, curTime); - } - - protected void updateBooleanLastValue(boolean value, long curTime) { - hasCandidateResult = true; - super.updateBooleanLastValue(value, curTime); - } - - protected void updateBinaryLastValue(Binary value, long curTime) { - hasCandidateResult = true; - super.updateBinaryLastValue(value, curTime); - } } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/MaxTimeAccumulator.java b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/MaxTimeAccumulator.java index ce47210337e9..2b8eb22de763 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/MaxTimeAccumulator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/MaxTimeAccumulator.java @@ -30,6 +30,7 @@ public class MaxTimeAccumulator implements Accumulator { protected long maxTime = Long.MIN_VALUE; + protected boolean initResult = false; public MaxTimeAccumulator() {} @@ -39,7 +40,7 @@ public MaxTimeAccumulator() {} public void addInput(Column[] column, TimeRange timeRange) { for (int i = 0; i < column[0].getPositionCount(); i++) { long curTime = column[0].getLong(i); - if (curTime >= timeRange.getMin() && curTime < timeRange.getMax() && !column[1].isNull(i)) { + if (timeRange.contains(curTime) && !column[1].isNull(i)) { updateMaxTime(curTime); } } @@ -49,6 +50,9 @@ public void addInput(Column[] column, TimeRange timeRange) { @Override public void addIntermediate(Column[] partialResult) { checkArgument(partialResult.length == 1, "partialResult of MaxTime should be 1"); + if (partialResult[0].isNull(0)) { + return; + } updateMaxTime(partialResult[0].getLong(0)); } @@ -60,6 +64,10 @@ public void addStatistics(Statistics statistics) { // finalResult should be single column, like: | finalMaxTime | @Override public void setFinal(Column finalResult) { + if (finalResult.isNull(0)) { + return; + } + initResult = true; maxTime = finalResult.getLong(0); } @@ -67,16 +75,25 @@ public void setFinal(Column finalResult) { @Override public void outputIntermediate(ColumnBuilder[] columnBuilders) { checkArgument(columnBuilders.length == 1, "partialResult of MaxTime should be 1"); - columnBuilders[0].writeLong(maxTime); + if (!initResult) { + columnBuilders[0].appendNull(); + } else { + columnBuilders[0].writeLong(maxTime); + } } @Override public void outputFinal(ColumnBuilder columnBuilder) { - columnBuilder.writeLong(maxTime); + if (!initResult) { + columnBuilder.appendNull(); + } else { + columnBuilder.writeLong(maxTime); + } } @Override public void reset() { + initResult = false; this.maxTime = Long.MIN_VALUE; } @@ -96,6 +113,7 @@ public TSDataType getFinalType() { } protected void updateMaxTime(long curTime) { + initResult = true; maxTime = Math.max(maxTime, curTime); } } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/MaxTimeDescAccumulator.java b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/MaxTimeDescAccumulator.java index b753e9ca3b8c..eae6e9f85994 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/MaxTimeDescAccumulator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/MaxTimeDescAccumulator.java @@ -24,15 +24,13 @@ public class MaxTimeDescAccumulator extends MaxTimeAccumulator { - private boolean hasCandidateResult = false; - // Column should be like: | Time | Value | // Value is used to judge isNull() @Override public void addInput(Column[] column, TimeRange timeRange) { for (int i = 0; i < column[0].getPositionCount(); i++) { long curTime = column[0].getLong(i); - if (curTime >= timeRange.getMin() && curTime < timeRange.getMax() && !column[1].isNull(i)) { + if (timeRange.contains(curTime) && !column[1].isNull(i)) { updateMaxTime(curTime); break; } @@ -41,17 +39,6 @@ public void addInput(Column[] column, TimeRange timeRange) { @Override public boolean hasFinalResult() { - return hasCandidateResult; - } - - @Override - public void reset() { - hasCandidateResult = false; - super.reset(); - } - - protected void updateMaxTime(long curTime) { - hasCandidateResult = true; - super.updateMaxTime(curTime); + return initResult; } } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/MaxValueAccumulator.java b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/MaxValueAccumulator.java index c07480a13d57..343a03f093a9 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/MaxValueAccumulator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/MaxValueAccumulator.java @@ -69,6 +69,9 @@ public void addInput(Column[] column, TimeRange timeRange) { @Override public void addIntermediate(Column[] partialResult) { checkArgument(partialResult.length == 1, "partialResult of MaxValue should be 1"); + if (partialResult[0].isNull(0)) { + return; + } switch (seriesDataType) { case INT32: updateIntResult(partialResult[0].getInt(0)); @@ -116,6 +119,9 @@ public void addStatistics(Statistics statistics) { // finalResult should be single column, like: | finalCountValue | @Override public void setFinal(Column finalResult) { + if (finalResult.isNull(0)) { + return; + } maxResult.setObject(finalResult.getObject(0)); } @@ -123,6 +129,10 @@ public void setFinal(Column finalResult) { @Override public void outputIntermediate(ColumnBuilder[] columnBuilders) { checkArgument(columnBuilders.length == 1, "partialResult of MaxValue should be 1"); + if (!initResult) { + columnBuilders[0].appendNull(); + return; + } switch (seriesDataType) { case INT32: columnBuilders[0].writeInt(maxResult.getInt()); @@ -146,6 +156,10 @@ public void outputIntermediate(ColumnBuilder[] columnBuilders) { @Override public void outputFinal(ColumnBuilder columnBuilder) { + if (!initResult) { + columnBuilder.appendNull(); + return; + } switch (seriesDataType) { case INT32: columnBuilder.writeInt(maxResult.getInt()); @@ -192,7 +206,7 @@ private void addIntInput(Column[] column, TimeRange timeRange) { TimeColumn timeColumn = (TimeColumn) column[0]; for (int i = 0; i < timeColumn.getPositionCount(); i++) { long curTime = timeColumn.getLong(i); - if (curTime >= timeRange.getMax() || curTime < timeRange.getMin()) { + if (curTime > timeRange.getMax() || curTime < timeRange.getMin()) { break; } if (!column[1].isNull(i)) { @@ -212,7 +226,7 @@ private void addLongInput(Column[] column, TimeRange timeRange) { TimeColumn timeColumn = (TimeColumn) column[0]; for (int i = 0; i < timeColumn.getPositionCount(); i++) { long curTime = timeColumn.getLong(i); - if (curTime >= timeRange.getMax() || curTime < timeRange.getMin()) { + if (curTime > timeRange.getMax() || curTime < timeRange.getMin()) { break; } if (!column[1].isNull(i)) { @@ -232,7 +246,7 @@ private void addFloatInput(Column[] column, TimeRange timeRange) { TimeColumn timeColumn = (TimeColumn) column[0]; for (int i = 0; i < timeColumn.getPositionCount(); i++) { long curTime = timeColumn.getLong(i); - if (curTime >= timeRange.getMax() || curTime < timeRange.getMin()) { + if (curTime > timeRange.getMax() || curTime < timeRange.getMin()) { break; } if (!column[1].isNull(i)) { @@ -252,7 +266,7 @@ private void addDoubleInput(Column[] column, TimeRange timeRange) { TimeColumn timeColumn = (TimeColumn) column[0]; for (int i = 0; i < timeColumn.getPositionCount(); i++) { long curTime = timeColumn.getLong(i); - if (curTime >= timeRange.getMax() || curTime < timeRange.getMin()) { + if (curTime > timeRange.getMax() || curTime < timeRange.getMin()) { break; } if (!column[1].isNull(i)) { diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/MinTimeAccumulator.java b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/MinTimeAccumulator.java index a6af85093d11..d2a3a8e83726 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/MinTimeAccumulator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/MinTimeAccumulator.java @@ -40,7 +40,7 @@ public MinTimeAccumulator() {} public void addInput(Column[] column, TimeRange timeRange) { for (int i = 0; i < column[0].getPositionCount(); i++) { long curTime = column[0].getLong(i); - if (curTime >= timeRange.getMin() && curTime < timeRange.getMax() && !column[1].isNull(i)) { + if (timeRange.contains(curTime) && !column[1].isNull(i)) { updateMinTime(curTime); break; } @@ -51,6 +51,9 @@ public void addInput(Column[] column, TimeRange timeRange) { @Override public void addIntermediate(Column[] partialResult) { checkArgument(partialResult.length == 1, "partialResult of MinTime should be 1"); + if (partialResult[0].isNull(0)) { + return; + } updateMinTime(partialResult[0].getLong(0)); } @@ -62,6 +65,9 @@ public void addStatistics(Statistics statistics) { // finalResult should be single column, like: | finalMinTime | @Override public void setFinal(Column finalResult) { + if (finalResult.isNull(0)) { + return; + } minTime = finalResult.getLong(0); } @@ -69,12 +75,20 @@ public void setFinal(Column finalResult) { @Override public void outputIntermediate(ColumnBuilder[] columnBuilders) { checkArgument(columnBuilders.length == 1, "partialResult of MinTime should be 1"); - columnBuilders[0].writeLong(minTime); + if (!hasCandidateResult) { + columnBuilders[0].appendNull(); + } else { + columnBuilders[0].writeLong(minTime); + } } @Override public void outputFinal(ColumnBuilder columnBuilder) { - columnBuilder.writeLong(minTime); + if (!hasCandidateResult) { + columnBuilder.appendNull(); + } else { + columnBuilder.writeLong(minTime); + } } @Override diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/MinTimeDescAccumulator.java b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/MinTimeDescAccumulator.java index e97fa3ca7037..615a05686ccd 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/MinTimeDescAccumulator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/MinTimeDescAccumulator.java @@ -28,7 +28,7 @@ public class MinTimeDescAccumulator extends MinTimeAccumulator { public void addInput(Column[] column, TimeRange timeRange) { for (int i = 0; i < column[0].getPositionCount(); i++) { long curTime = column[0].getLong(i); - if (curTime >= timeRange.getMin() && curTime < timeRange.getMax()) { + if (timeRange.contains(curTime)) { updateMinTime(curTime); } } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/MinValueAccumulator.java b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/MinValueAccumulator.java index 8b24de07ea3c..b70d15310434 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/MinValueAccumulator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/MinValueAccumulator.java @@ -69,6 +69,9 @@ public void addInput(Column[] column, TimeRange timeRange) { @Override public void addIntermediate(Column[] partialResult) { checkArgument(partialResult.length == 1, "partialResult of MinValue should be 1"); + if (partialResult[0].isNull(0)) { + return; + } switch (seriesDataType) { case INT32: updateIntResult(partialResult[0].getInt(0)); @@ -116,6 +119,9 @@ public void addStatistics(Statistics statistics) { // finalResult should be single column, like: | finalCountValue | @Override public void setFinal(Column finalResult) { + if (finalResult.isNull(0)) { + return; + } minResult.setObject(finalResult.getObject(0)); } @@ -123,6 +129,10 @@ public void setFinal(Column finalResult) { @Override public void outputIntermediate(ColumnBuilder[] columnBuilders) { checkArgument(columnBuilders.length == 1, "partialResult of MinValue should be 1"); + if (!initResult) { + columnBuilders[0].appendNull(); + return; + } switch (seriesDataType) { case INT32: columnBuilders[0].writeInt(minResult.getInt()); @@ -146,6 +156,10 @@ public void outputIntermediate(ColumnBuilder[] columnBuilders) { @Override public void outputFinal(ColumnBuilder columnBuilder) { + if (!initResult) { + columnBuilder.appendNull(); + return; + } switch (seriesDataType) { case INT32: columnBuilder.writeInt(minResult.getInt()); @@ -192,7 +206,7 @@ private void addIntInput(Column[] column, TimeRange timeRange) { TimeColumn timeColumn = (TimeColumn) column[0]; for (int i = 0; i < timeColumn.getPositionCount(); i++) { long curTime = timeColumn.getLong(i); - if (curTime >= timeRange.getMax() || curTime < timeRange.getMin()) { + if (curTime > timeRange.getMax() || curTime < timeRange.getMin()) { break; } if (!column[1].isNull(i)) { @@ -212,7 +226,7 @@ private void addLongInput(Column[] column, TimeRange timeRange) { TimeColumn timeColumn = (TimeColumn) column[0]; for (int i = 0; i < timeColumn.getPositionCount(); i++) { long curTime = timeColumn.getLong(i); - if (curTime >= timeRange.getMax() || curTime < timeRange.getMin()) { + if (curTime > timeRange.getMax() || curTime < timeRange.getMin()) { break; } if (!column[1].isNull(i)) { @@ -232,7 +246,7 @@ private void addFloatInput(Column[] column, TimeRange timeRange) { TimeColumn timeColumn = (TimeColumn) column[0]; for (int i = 0; i < timeColumn.getPositionCount(); i++) { long curTime = timeColumn.getLong(i); - if (curTime >= timeRange.getMax() || curTime < timeRange.getMin()) { + if (curTime > timeRange.getMax() || curTime < timeRange.getMin()) { break; } if (!column[1].isNull(i)) { @@ -252,7 +266,7 @@ private void addDoubleInput(Column[] column, TimeRange timeRange) { TimeColumn timeColumn = (TimeColumn) column[0]; for (int i = 0; i < timeColumn.getPositionCount(); i++) { long curTime = timeColumn.getLong(i); - if (curTime >= timeRange.getMax() || curTime < timeRange.getMin()) { + if (curTime > timeRange.getMax() || curTime < timeRange.getMin()) { break; } if (!column[1].isNull(i)) { diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/SumAccumulator.java b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/SumAccumulator.java index 132fb017a9f9..057292b6246c 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/SumAccumulator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/SumAccumulator.java @@ -34,6 +34,7 @@ public class SumAccumulator implements Accumulator { private TSDataType seriesDataType; private double sumValue = 0; + private boolean initResult = false; public SumAccumulator(TSDataType seriesDataType) { this.seriesDataType = seriesDataType; @@ -67,11 +68,16 @@ public void addInput(Column[] column, TimeRange timeRange) { @Override public void addIntermediate(Column[] partialResult) { checkArgument(partialResult.length == 1, "partialResult of Sum should be 1"); + if (partialResult[0].isNull(0)) { + return; + } + initResult = true; sumValue += partialResult[0].getDouble(0); } @Override public void addStatistics(Statistics statistics) { + initResult = true; if (statistics instanceof IntegerStatistics) { sumValue += statistics.getSumLongValue(); } else { @@ -83,6 +89,9 @@ public void addStatistics(Statistics statistics) { @Override public void setFinal(Column finalResult) { reset(); + if (finalResult.isNull(0)) { + return; + } sumValue = finalResult.getDouble(0); } @@ -90,16 +99,25 @@ public void setFinal(Column finalResult) { @Override public void outputIntermediate(ColumnBuilder[] columnBuilders) { checkArgument(columnBuilders.length == 1, "partialResult of Sum should be 1"); - columnBuilders[0].writeDouble(sumValue); + if (!initResult) { + columnBuilders[0].appendNull(); + } else { + columnBuilders[0].writeDouble(sumValue); + } } @Override public void outputFinal(ColumnBuilder columnBuilder) { - columnBuilder.writeDouble(sumValue); + if (!initResult) { + columnBuilder.appendNull(); + } else { + columnBuilder.writeDouble(sumValue); + } } @Override public void reset() { + initResult = false; this.sumValue = 0; } @@ -122,10 +140,11 @@ private void addIntInput(Column[] column, TimeRange timeRange) { TimeColumn timeColumn = (TimeColumn) column[0]; for (int i = 0; i < timeColumn.getPositionCount(); i++) { long curTime = timeColumn.getLong(i); - if (curTime >= timeRange.getMax() || curTime < timeRange.getMin()) { + if (curTime > timeRange.getMax() || curTime < timeRange.getMin()) { break; } if (!column[1].isNull(i)) { + initResult = true; sumValue += column[1].getInt(i); } } @@ -135,10 +154,11 @@ private void addLongInput(Column[] column, TimeRange timeRange) { TimeColumn timeColumn = (TimeColumn) column[0]; for (int i = 0; i < timeColumn.getPositionCount(); i++) { long curTime = timeColumn.getLong(i); - if (curTime >= timeRange.getMax() || curTime < timeRange.getMin()) { + if (curTime > timeRange.getMax() || curTime < timeRange.getMin()) { break; } if (!column[1].isNull(i)) { + initResult = true; sumValue += column[1].getLong(i); } } @@ -148,10 +168,11 @@ private void addFloatInput(Column[] column, TimeRange timeRange) { TimeColumn timeColumn = (TimeColumn) column[0]; for (int i = 0; i < timeColumn.getPositionCount(); i++) { long curTime = timeColumn.getLong(i); - if (curTime >= timeRange.getMax() || curTime < timeRange.getMin()) { + if (curTime > timeRange.getMax() || curTime < timeRange.getMin()) { break; } if (!column[1].isNull(i)) { + initResult = true; sumValue += column[1].getFloat(i); } } @@ -161,10 +182,11 @@ private void addDoubleInput(Column[] column, TimeRange timeRange) { TimeColumn timeColumn = (TimeColumn) column[0]; for (int i = 0; i < timeColumn.getPositionCount(); i++) { long curTime = timeColumn.getLong(i); - if (curTime >= timeRange.getMax() || curTime < timeRange.getMin()) { + if (curTime > timeRange.getMax() || curTime < timeRange.getMin()) { break; } if (!column[1].isNull(i)) { + initResult = true; sumValue += column[1].getDouble(i); } } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/timerangeiterator/AggrWindowIterator.java b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/timerangeiterator/AggrWindowIterator.java new file mode 100644 index 000000000000..d53678bad2c5 --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/timerangeiterator/AggrWindowIterator.java @@ -0,0 +1,172 @@ +/* + * 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.iotdb.db.mpp.aggregation.timerangeiterator; + +import org.apache.iotdb.db.qp.utils.DatetimeUtils; +import org.apache.iotdb.tsfile.read.common.TimeRange; + +import static org.apache.iotdb.db.qp.utils.DatetimeUtils.MS_TO_MONTH; + +/** + * This class iteratively generates aggregated time windows. + * + *

For example, startTime = 0, endTime = 10, interval = 5, slidingStep = 3, leftCloseRightOpen, + * return [0,5],[3,7],[6,9],[9,9] + */ +public class AggrWindowIterator implements ITimeRangeIterator { + + private final long startTime; + private final long endTime; + private final long interval; + private final long slidingStep; + + private final boolean isAscending; + private final boolean isSlidingStepByMonth; + private final boolean isIntervalByMonth; + private final boolean leftCRightO; + + private TimeRange curTimeRange; + + public AggrWindowIterator( + long startTime, + long endTime, + long interval, + long slidingStep, + boolean isAscending, + boolean isSlidingStepByMonth, + boolean isIntervalByMonth, + boolean leftCRightO) { + this.startTime = startTime; + this.endTime = endTime; + this.interval = interval; + this.slidingStep = slidingStep; + this.isAscending = isAscending; + this.isSlidingStepByMonth = isSlidingStepByMonth; + this.isIntervalByMonth = isIntervalByMonth; + this.leftCRightO = leftCRightO; + } + + @Override + public TimeRange getFirstTimeRange() { + if (isAscending) { + return getLeftmostTimeRange(); + } else { + return getRightmostTimeRange(); + } + } + + private TimeRange getLeftmostTimeRange() { + long retEndTime; + if (isIntervalByMonth) { + // calculate interval length by natural month based on startTime + // ie. startTIme = 1/31, interval = 1mo, curEndTime will be set to 2/29 + retEndTime = Math.min(DatetimeUtils.calcIntervalByMonth(startTime, interval), endTime); + } else { + retEndTime = Math.min(startTime + interval, endTime); + } + return new TimeRange(startTime, retEndTime); + } + + private TimeRange getRightmostTimeRange() { + long retStartTime; + long retEndTime; + long queryRange = endTime - startTime; + long intervalNum; + + if (isSlidingStepByMonth) { + intervalNum = (long) Math.ceil(queryRange / (double) (slidingStep * MS_TO_MONTH)); + retStartTime = DatetimeUtils.calcIntervalByMonth(startTime, intervalNum * slidingStep); + while (retStartTime >= endTime) { + intervalNum -= 1; + retStartTime = DatetimeUtils.calcIntervalByMonth(startTime, intervalNum * slidingStep); + } + } else { + intervalNum = (long) Math.ceil(queryRange / (double) slidingStep); + retStartTime = slidingStep * (intervalNum - 1) + startTime; + } + + if (isIntervalByMonth) { + // calculate interval length by natural month based on curStartTime + // ie. startTIme = 1/31, interval = 1mo, curEndTime will be set to 2/29 + retEndTime = Math.min(DatetimeUtils.calcIntervalByMonth(retStartTime, interval), endTime); + } else { + retEndTime = Math.min(retStartTime + interval, endTime); + } + return new TimeRange(retStartTime, retEndTime); + } + + @Override + public boolean hasNextTimeRange() { + if (curTimeRange == null) { + curTimeRange = getFirstTimeRange(); + return true; + } + + long retStartTime, retEndTime; + long curStartTime = curTimeRange.getMin(); + if (isAscending) { + if (isSlidingStepByMonth) { + retStartTime = DatetimeUtils.calcIntervalByMonth(curStartTime, (int) (slidingStep)); + } else { + retStartTime = curStartTime + slidingStep; + } + // This is an open interval , [0-100) + if (retStartTime >= endTime) { + return false; + } + } else { + if (isSlidingStepByMonth) { + retStartTime = DatetimeUtils.calcIntervalByMonth(curStartTime, (int) (-slidingStep)); + } else { + retStartTime = curStartTime - slidingStep; + } + if (retStartTime < startTime) { + return false; + } + } + + if (isIntervalByMonth) { + retEndTime = DatetimeUtils.calcIntervalByMonth(retStartTime, (int) (interval)); + } else { + retEndTime = retStartTime + interval; + } + retEndTime = Math.min(retEndTime, endTime); + curTimeRange = new TimeRange(retStartTime, retEndTime); + return true; + } + + @Override + public TimeRange nextTimeRange() { + if (curTimeRange != null || hasNextTimeRange()) { + return getFinalTimeRange(curTimeRange, leftCRightO); + } + return null; + } + + @Override + public boolean isAscending() { + return isAscending; + } + + @Override + public long currentOutputTime() { + return leftCRightO ? curTimeRange.getMin() : curTimeRange.getMax(); + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/timerangeiterator/ITimeRangeIterator.java b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/timerangeiterator/ITimeRangeIterator.java new file mode 100644 index 000000000000..ccc999e81068 --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/timerangeiterator/ITimeRangeIterator.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.db.mpp.aggregation.timerangeiterator; + +import org.apache.iotdb.tsfile.read.common.TimeRange; + +/** + * This interface used for iteratively generating aggregated time windows in GROUP BY query. + * + *

It will return a leftCloseRightClose time window, by decreasing maxTime if leftCloseRightOpen + * and increasing minTime if leftOpenRightClose. + */ +public interface ITimeRangeIterator { + + /** return the first time range by sorting order */ + TimeRange getFirstTimeRange(); + + /** @return whether current iterator has next time range */ + boolean hasNextTimeRange(); + /** + * return the next time range according to curStartTime (the start time of the last returned time + * range) + */ + TimeRange nextTimeRange(); + + boolean isAscending(); + + default TimeRange getFinalTimeRange(TimeRange timeRange, boolean leftCRightO) { + return leftCRightO + ? new TimeRange(timeRange.getMin(), timeRange.getMax() - 1) + : new TimeRange(timeRange.getMin() + 1, timeRange.getMax()); + } + + /** + * As there is only one timestamp can be output for a time range, this method will return the + * output time based on leftCloseRightOpen or not. + * + * @return minTime if leftCloseRightOpen, else maxTime. + */ + long currentOutputTime(); +} diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/timerangeiterator/PreAggrWindowIterator.java b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/timerangeiterator/PreAggrWindowIterator.java new file mode 100644 index 000000000000..ab018e3cb733 --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/timerangeiterator/PreAggrWindowIterator.java @@ -0,0 +1,169 @@ +/* + * 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.iotdb.db.mpp.aggregation.timerangeiterator; + +import org.apache.iotdb.tsfile.read.common.TimeRange; + +/** + * This class iteratively generates pre-aggregated time windows. + * + *

For example, startTime = 0, endTime = 11, interval = 5, slidingStep = 3, return + * [0,2),[2,3),[3,5),[5,6),[6,8),[8,9),[9,10) + */ +public class PreAggrWindowIterator implements ITimeRangeIterator { + + private final long startTime; + private final long endTime; + private final long interval; + private final long slidingStep; + + private final boolean isAscending; + private final boolean leftCRightO; + + private long curInterval; + private long curSlidingStep; + private boolean isIntervalCyclicChange = false; + private int intervalCnt = 0; + + private TimeRange curTimeRange; + + public PreAggrWindowIterator( + long startTime, + long endTime, + long interval, + long slidingStep, + boolean isAscending, + boolean leftCRightO) { + this.startTime = startTime; + this.endTime = endTime; + this.interval = interval; + this.slidingStep = slidingStep; + this.isAscending = isAscending; + this.leftCRightO = leftCRightO; + initIntervalAndStep(); + } + + @Override + public TimeRange getFirstTimeRange() { + if (isAscending) { + return getLeftmostTimeRange(); + } else { + return getRightmostTimeRange(); + } + } + + private TimeRange getLeftmostTimeRange() { + long retEndTime = Math.min(startTime + curInterval, endTime); + updateIntervalAndStep(); + return new TimeRange(startTime, retEndTime); + } + + private TimeRange getRightmostTimeRange() { + long retStartTime; + long retEndTime; + long intervalNum = (long) Math.ceil((endTime - startTime) / (double) slidingStep); + retStartTime = slidingStep * (intervalNum - 1) + startTime; + if (isIntervalCyclicChange && endTime - retStartTime > interval % slidingStep) { + retStartTime += interval % slidingStep; + updateIntervalAndStep(); + } + retEndTime = Math.min(retStartTime + curInterval, endTime); + updateIntervalAndStep(); + return new TimeRange(retStartTime, retEndTime); + } + + @Override + public boolean hasNextTimeRange() { + if (curTimeRange == null) { + curTimeRange = getFirstTimeRange(); + return true; + } + + long retStartTime, retEndTime; + long curStartTime = curTimeRange.getMin(); + if (isAscending) { + retStartTime = curStartTime + curSlidingStep; + // This is an open interval , [0-100) + if (retStartTime >= endTime) { + return false; + } + } else { + retStartTime = curStartTime - curSlidingStep; + if (retStartTime < startTime) { + return false; + } + } + retEndTime = Math.min(retStartTime + curInterval, endTime); + updateIntervalAndStep(); + curTimeRange = new TimeRange(retStartTime, retEndTime); + return true; + } + + @Override + public TimeRange nextTimeRange() { + if (curTimeRange != null || hasNextTimeRange()) { + return getFinalTimeRange(curTimeRange, leftCRightO); + } + return null; + } + + private void initIntervalAndStep() { + if (slidingStep >= interval) { + curInterval = interval; + curSlidingStep = slidingStep; + } else if (interval % slidingStep == 0) { + curInterval = slidingStep; + curSlidingStep = slidingStep; + } else { + isIntervalCyclicChange = true; + curInterval = interval % slidingStep; + curSlidingStep = curInterval; + } + } + + private void updateIntervalAndStep() { + if (!isIntervalCyclicChange) { + return; + } + + if (isAscending) { + curSlidingStep = curInterval; + } + intervalCnt++; + if ((intervalCnt & 1) == 1) { + curInterval = slidingStep - interval % slidingStep; + } else { + curInterval = interval % slidingStep; + } + if (!isAscending) { + curSlidingStep = curInterval; + } + } + + @Override + public boolean isAscending() { + return isAscending; + } + + @Override + public long currentOutputTime() { + return leftCRightO ? curTimeRange.getMin() : curTimeRange.getMax(); + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/timerangeiterator/PreAggrWindowWithNaturalMonthIterator.java b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/timerangeiterator/PreAggrWindowWithNaturalMonthIterator.java new file mode 100644 index 000000000000..0a0d63081166 --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/timerangeiterator/PreAggrWindowWithNaturalMonthIterator.java @@ -0,0 +1,143 @@ +/* + * 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.iotdb.db.mpp.aggregation.timerangeiterator; + +import org.apache.iotdb.db.utils.datastructure.TimeSelector; +import org.apache.iotdb.tsfile.read.common.TimeRange; + +public class PreAggrWindowWithNaturalMonthIterator implements ITimeRangeIterator { + + private static final int HEAP_MAX_SIZE = 100; + + private final boolean isAscending; + private final boolean leftCRightO; + private final TimeSelector timeBoundaryHeap; + + private final AggrWindowIterator aggrWindowIterator; + private long curStartTimeForIterator; + + private long lastEndTime; + private TimeRange curTimeRange; + + public PreAggrWindowWithNaturalMonthIterator( + long startTime, + long endTime, + long interval, + long slidingStep, + boolean isAscending, + boolean isSlidingStepByMonth, + boolean isIntervalByMonth, + boolean leftCRightO) { + this.isAscending = isAscending; + this.timeBoundaryHeap = new TimeSelector(HEAP_MAX_SIZE, isAscending); + this.aggrWindowIterator = + new AggrWindowIterator( + startTime, + endTime, + interval, + slidingStep, + isAscending, + isSlidingStepByMonth, + isIntervalByMonth, + leftCRightO); + this.leftCRightO = leftCRightO; + initHeap(); + } + + @Override + public TimeRange getFirstTimeRange() { + long retStartTime = timeBoundaryHeap.pollFirst(); + lastEndTime = timeBoundaryHeap.first(); + return new TimeRange(retStartTime, lastEndTime); + } + + @Override + public boolean hasNextTimeRange() { + if (curTimeRange == null) { + curTimeRange = getFirstTimeRange(); + return true; + } + + if (lastEndTime >= curStartTimeForIterator) { + tryToExpandHeap(); + } + if (timeBoundaryHeap.isEmpty()) { + return false; + } + long retStartTime = timeBoundaryHeap.pollFirst(); + if (retStartTime >= curStartTimeForIterator) { + tryToExpandHeap(); + } + if (timeBoundaryHeap.isEmpty()) { + return false; + } + lastEndTime = timeBoundaryHeap.first(); + curTimeRange = new TimeRange(retStartTime, lastEndTime); + return true; + } + + @Override + public TimeRange nextTimeRange() { + if (curTimeRange != null || hasNextTimeRange()) { + return getFinalTimeRange(curTimeRange, leftCRightO); + } + return null; + } + + private void initHeap() { + TimeRange firstTimeRange = aggrWindowIterator.nextTimeRange(); + if (leftCRightO) { + timeBoundaryHeap.add(firstTimeRange.getMin()); + timeBoundaryHeap.add(firstTimeRange.getMax() + 1); + curStartTimeForIterator = firstTimeRange.getMin(); + } else { + timeBoundaryHeap.add(firstTimeRange.getMin() - 1); + timeBoundaryHeap.add(firstTimeRange.getMax()); + curStartTimeForIterator = firstTimeRange.getMin() - 1; + } + tryToExpandHeap(); + } + + private void tryToExpandHeap() { + TimeRange timeRangeToExpand = null; + while (aggrWindowIterator.hasNextTimeRange() && timeBoundaryHeap.size() < HEAP_MAX_SIZE) { + timeRangeToExpand = aggrWindowIterator.nextTimeRange(); + if (leftCRightO) { + timeBoundaryHeap.add(timeRangeToExpand.getMin()); + timeBoundaryHeap.add(timeRangeToExpand.getMax() + 1); + curStartTimeForIterator = timeRangeToExpand.getMin(); + } else { + timeBoundaryHeap.add(timeRangeToExpand.getMin() - 1); + timeBoundaryHeap.add(timeRangeToExpand.getMax()); + curStartTimeForIterator = timeRangeToExpand.getMin() - 1; + } + } + } + + @Override + public boolean isAscending() { + return isAscending; + } + + @Override + public long currentOutputTime() { + return leftCRightO ? curTimeRange.getMin() : curTimeRange.getMax(); + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/utils/timerangeiterator/SingleTimeWindowIterator.java b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/timerangeiterator/SingleTimeWindowIterator.java similarity index 92% rename from server/src/main/java/org/apache/iotdb/db/utils/timerangeiterator/SingleTimeWindowIterator.java rename to server/src/main/java/org/apache/iotdb/db/mpp/aggregation/timerangeiterator/SingleTimeWindowIterator.java index 3470f98834ea..2145b51f454f 100644 --- a/server/src/main/java/org/apache/iotdb/db/utils/timerangeiterator/SingleTimeWindowIterator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/timerangeiterator/SingleTimeWindowIterator.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.utils.timerangeiterator; +package org.apache.iotdb.db.mpp.aggregation.timerangeiterator; import org.apache.iotdb.tsfile.read.common.TimeRange; @@ -62,4 +62,9 @@ public TimeRange nextTimeRange() { public boolean isAscending() { return false; } + + @Override + public long currentOutputTime() { + return curTimeRange.getMin(); + } } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/timerangeiterator/TimeRangeIteratorFactory.java b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/timerangeiterator/TimeRangeIteratorFactory.java new file mode 100644 index 000000000000..7a270eb6cde8 --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/timerangeiterator/TimeRangeIteratorFactory.java @@ -0,0 +1,73 @@ +/* + * 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.iotdb.db.mpp.aggregation.timerangeiterator; + +import static org.apache.iotdb.db.qp.utils.DatetimeUtils.MS_TO_MONTH; + +public class TimeRangeIteratorFactory { + + private TimeRangeIteratorFactory() {} + + /** + * The method returns different implements of ITimeRangeIterator depending on the parameters. + * + *

Note: interval and slidingStep stand for the milliseconds if not grouped by month, or the + * month count if grouped by month. + */ + public static ITimeRangeIterator getTimeRangeIterator( + long startTime, + long endTime, + long interval, + long slidingStep, + boolean isAscending, + boolean isIntervalByMonth, + boolean isSlidingStepByMonth, + boolean leftCRightO, + boolean isPreAggr) { + long tmpInterval = isIntervalByMonth ? interval * MS_TO_MONTH : interval; + long tmpSlidingStep = isSlidingStepByMonth ? slidingStep * MS_TO_MONTH : slidingStep; + if (isPreAggr && tmpInterval > tmpSlidingStep) { + if (!isIntervalByMonth && !isSlidingStepByMonth) { + return new PreAggrWindowIterator( + startTime, endTime, interval, slidingStep, isAscending, leftCRightO); + } else { + return new PreAggrWindowWithNaturalMonthIterator( + startTime, + endTime, + interval, + slidingStep, + isAscending, + isSlidingStepByMonth, + isIntervalByMonth, + leftCRightO); + } + } else { + return new AggrWindowIterator( + startTime, + endTime, + interval, + slidingStep, + isAscending, + isSlidingStepByMonth, + isIntervalByMonth, + leftCRightO); + } + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/AggregateOperator.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/AggregateOperator.java index 71e7817b9452..c860cd745022 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/AggregateOperator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/AggregateOperator.java @@ -19,25 +19,62 @@ package org.apache.iotdb.db.mpp.execution.operator.process; import org.apache.iotdb.db.mpp.aggregation.Aggregator; +import org.apache.iotdb.db.mpp.aggregation.timerangeiterator.ITimeRangeIterator; import org.apache.iotdb.db.mpp.execution.operator.Operator; import org.apache.iotdb.db.mpp.execution.operator.OperatorContext; +import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.GroupByTimeParameter; +import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; +import org.apache.iotdb.tsfile.read.common.TimeRange; import org.apache.iotdb.tsfile.read.common.block.TsBlock; +import org.apache.iotdb.tsfile.read.common.block.TsBlockBuilder; +import org.apache.iotdb.tsfile.read.common.block.column.ColumnBuilder; +import org.apache.iotdb.tsfile.read.common.block.column.TimeColumnBuilder; import com.google.common.util.concurrent.ListenableFuture; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import static org.apache.iotdb.db.mpp.execution.operator.source.SeriesAggregateScanOperator.initTimeRangeIterator; + +/** + * AggregateOperator can process the situation: aggregation of intermediate aggregate result, it + * will output one result based on time interval. One intermediate tsBlock input will only contain + * the result of one time interval exactly. + */ public class AggregateOperator implements ProcessOperator { private final OperatorContext operatorContext; private final List aggregators; private final List children; + private final int inputOperatorsCount; + private final TsBlock[] inputTsBlocks; + private final TsBlockBuilder tsBlockBuilder; + + private ITimeRangeIterator timeRangeIterator; + // current interval of aggregation window [curStartTime, curEndTime) + private TimeRange curTimeRange; + public AggregateOperator( - OperatorContext operatorContext, List aggregators, List children) { + OperatorContext operatorContext, + List aggregators, + List children, + boolean ascending, + GroupByTimeParameter groupByTimeParameter) { this.operatorContext = operatorContext; this.aggregators = aggregators; this.children = children; + + this.inputOperatorsCount = children.size(); + this.inputTsBlocks = new TsBlock[inputOperatorsCount]; + List dataTypes = new ArrayList<>(); + for (Aggregator aggregator : aggregators) { + dataTypes.addAll(Arrays.asList(aggregator.getOutputType())); + } + tsBlockBuilder = new TsBlockBuilder(dataTypes); + this.timeRangeIterator = initTimeRangeIterator(groupByTimeParameter, ascending); } @Override @@ -47,26 +84,67 @@ public OperatorContext getOperatorContext() { @Override public ListenableFuture isBlocked() { - return ProcessOperator.super.isBlocked(); + for (int i = 0; i < inputOperatorsCount; i++) { + ListenableFuture blocked = children.get(i).isBlocked(); + if (!blocked.isDone()) { + return blocked; + } + } + return NOT_BLOCKED; } @Override public TsBlock next() { - return null; + // update input tsBlock + curTimeRange = timeRangeIterator.nextTimeRange(); + for (int i = 0; i < inputOperatorsCount; i++) { + inputTsBlocks[i] = children.get(i).next(); + } + // consume current input tsBlocks + for (Aggregator aggregator : aggregators) { + aggregator.reset(); + aggregator.processTsBlocks(inputTsBlocks); + } + // output result from aggregator + return updateResultTsBlockFromAggregators(tsBlockBuilder, aggregators, timeRangeIterator); } @Override public boolean hasNext() { - return false; + return timeRangeIterator.hasNextTimeRange(); } @Override public void close() throws Exception { - ProcessOperator.super.close(); + for (Operator child : children) { + child.close(); + } } @Override public boolean isFinished() { - return false; + return !this.hasNext(); + } + + public static TsBlock updateResultTsBlockFromAggregators( + TsBlockBuilder tsBlockBuilder, + List aggregators, + ITimeRangeIterator timeRangeIterator) { + tsBlockBuilder.reset(); + TimeColumnBuilder timeColumnBuilder = tsBlockBuilder.getTimeColumnBuilder(); + // Use start time of current time range as time column + timeColumnBuilder.writeLong(timeRangeIterator.currentOutputTime()); + ColumnBuilder[] columnBuilders = tsBlockBuilder.getValueColumnBuilders(); + int columnIndex = 0; + for (Aggregator aggregator : aggregators) { + ColumnBuilder[] columnBuilder = new ColumnBuilder[aggregator.getOutputType().length]; + columnBuilder[0] = columnBuilders[columnIndex++]; + if (columnBuilder.length > 1) { + columnBuilder[1] = columnBuilders[columnIndex++]; + } + aggregator.outputResult(columnBuilder); + } + tsBlockBuilder.declarePosition(); + return tsBlockBuilder.build(); } } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/RawDataAggregateOperator.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/RawDataAggregateOperator.java new file mode 100644 index 000000000000..45f376eaa2a0 --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/RawDataAggregateOperator.java @@ -0,0 +1,194 @@ +/* + * 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.iotdb.db.mpp.execution.operator.process; + +import org.apache.iotdb.db.mpp.aggregation.Aggregator; +import org.apache.iotdb.db.mpp.aggregation.timerangeiterator.ITimeRangeIterator; +import org.apache.iotdb.db.mpp.execution.operator.Operator; +import org.apache.iotdb.db.mpp.execution.operator.OperatorContext; +import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.GroupByTimeParameter; +import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; +import org.apache.iotdb.tsfile.read.common.TimeRange; +import org.apache.iotdb.tsfile.read.common.block.TsBlock; +import org.apache.iotdb.tsfile.read.common.block.TsBlock.TsBlockSingleColumnIterator; +import org.apache.iotdb.tsfile.read.common.block.TsBlockBuilder; + +import com.google.common.util.concurrent.ListenableFuture; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.apache.iotdb.db.mpp.execution.operator.source.SeriesAggregateScanOperator.initTimeRangeIterator; + +/** + * RawDataAggregateOperator is used to process raw data tsBlock input calculating using value + * filter. It's possible that there is more than one tsBlock input in one time interval. And it's + * also possible that one tsBlock can cover multiple time intervals too. + * + *

Since raw data query with value filter is processed by FilterOperator above TimeJoinOperator, + * there we can see RawDataAggregateOperator as a one-to-one(one input, ont output) operator. + * + *

Return aggregation result in one time interval once. + */ +public class RawDataAggregateOperator implements ProcessOperator { + + private final OperatorContext operatorContext; + private final List aggregators; + private final Operator child; + private final boolean ascending; + private ITimeRangeIterator timeRangeIterator; + // current interval of aggregation window [curStartTime, curEndTime) + private TimeRange curTimeRange; + + private TsBlock preCachedData; + + // Using for building result tsBlock + private final TsBlockBuilder tsBlockBuilder; + + public RawDataAggregateOperator( + OperatorContext operatorContext, + List aggregators, + Operator child, + boolean ascending, + GroupByTimeParameter groupByTimeParameter) { + this.operatorContext = operatorContext; + this.aggregators = aggregators; + this.child = child; + this.ascending = ascending; + + List dataTypes = new ArrayList<>(); + for (Aggregator aggregator : aggregators) { + dataTypes.addAll(Arrays.asList(aggregator.getOutputType())); + } + tsBlockBuilder = new TsBlockBuilder(dataTypes); + this.timeRangeIterator = initTimeRangeIterator(groupByTimeParameter, ascending); + } + + @Override + public OperatorContext getOperatorContext() { + return operatorContext; + } + + @Override + public ListenableFuture isBlocked() { + return child.isBlocked(); + } + + @Override + public TsBlock next() { + // 1. Clear previous aggregation result + curTimeRange = timeRangeIterator.nextTimeRange(); + for (Aggregator aggregator : aggregators) { + aggregator.reset(); + aggregator.setTimeRange(curTimeRange); + } + + // 2. Calculate aggregation result based on current time window + while (!calcFromCacheData(curTimeRange)) { + if (child.hasNext()) { + preCachedData = child.next(); + } else { + break; + } + } + + // 3. Update result using aggregators + return AggregateOperator.updateResultTsBlockFromAggregators( + tsBlockBuilder, aggregators, timeRangeIterator); + } + + @Override + public boolean hasNext() { + return timeRangeIterator.hasNextTimeRange(); + } + + @Override + public void close() throws Exception { + child.close(); + } + + @Override + public boolean isFinished() { + return !this.hasNext(); + } + + /** @return if already get the result */ + private boolean calcFromCacheData(TimeRange curTimeRange) { + // check if the batchData does not contain points in current interval + if (preCachedData != null && satisfied(preCachedData, curTimeRange, ascending)) { + // skip points that cannot be calculated + preCachedData = skipOutOfTimeRangePoints(preCachedData, curTimeRange, ascending); + + for (Aggregator aggregator : aggregators) { + // current agg method has been calculated + if (aggregator.hasFinalResult()) { + continue; + } + + aggregator.processTsBlock(preCachedData); + } + } + // The result is calculated from the cache + return (preCachedData != null + && (ascending + ? preCachedData.getEndTime() > curTimeRange.getMax() + : preCachedData.getEndTime() < curTimeRange.getMin())) + || isEndCalc(aggregators); + } + + // skip points that cannot be calculated + public static TsBlock skipOutOfTimeRangePoints( + TsBlock tsBlock, TimeRange curTimeRange, boolean ascending) { + TsBlockSingleColumnIterator tsBlockIterator = tsBlock.getTsBlockSingleColumnIterator(); + if (ascending) { + while (tsBlockIterator.hasNext() && tsBlockIterator.currentTime() < curTimeRange.getMin()) { + tsBlockIterator.next(); + } + } else { + while (tsBlockIterator.hasNext() && tsBlockIterator.currentTime() > curTimeRange.getMax()) { + tsBlockIterator.next(); + } + } + return tsBlock.subTsBlock(tsBlockIterator.getRowIndex()); + } + + private boolean satisfied(TsBlock tsBlock, TimeRange timeRange, boolean ascending) { + TsBlockSingleColumnIterator tsBlockIterator = tsBlock.getTsBlockSingleColumnIterator(); + if (tsBlockIterator == null || !tsBlockIterator.hasNext()) { + return false; + } + + return ascending + ? (tsBlockIterator.getEndTime() >= timeRange.getMin() + && tsBlockIterator.currentTime() <= timeRange.getMax()) + : (tsBlockIterator.getEndTime() <= timeRange.getMax() + && tsBlockIterator.currentTime() >= timeRange.getMin()); + } + + public static boolean isEndCalc(List aggregators) { + for (Aggregator aggregator : aggregators) { + if (!aggregator.hasFinalResult()) { + return false; + } + } + return true; + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/source/SeriesAggregateScanOperator.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/source/SeriesAggregateScanOperator.java index fdc50a808bfd..92010c87ec50 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/source/SeriesAggregateScanOperator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/source/SeriesAggregateScanOperator.java @@ -21,20 +21,19 @@ import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.engine.querycontext.QueryDataSource; import org.apache.iotdb.db.mpp.aggregation.Aggregator; +import org.apache.iotdb.db.mpp.aggregation.timerangeiterator.ITimeRangeIterator; +import org.apache.iotdb.db.mpp.aggregation.timerangeiterator.SingleTimeWindowIterator; +import org.apache.iotdb.db.mpp.aggregation.timerangeiterator.TimeRangeIteratorFactory; import org.apache.iotdb.db.mpp.execution.operator.OperatorContext; +import org.apache.iotdb.db.mpp.execution.operator.process.AggregateOperator; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId; import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.GroupByTimeParameter; -import org.apache.iotdb.db.utils.timerangeiterator.ITimeRangeIterator; -import org.apache.iotdb.db.utils.timerangeiterator.SingleTimeWindowIterator; -import org.apache.iotdb.db.utils.timerangeiterator.TimeRangeIteratorFactory; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.file.metadata.statistics.Statistics; import org.apache.iotdb.tsfile.read.common.TimeRange; import org.apache.iotdb.tsfile.read.common.block.TsBlock; import org.apache.iotdb.tsfile.read.common.block.TsBlock.TsBlockSingleColumnIterator; import org.apache.iotdb.tsfile.read.common.block.TsBlockBuilder; -import org.apache.iotdb.tsfile.read.common.block.column.ColumnBuilder; -import org.apache.iotdb.tsfile.read.common.block.column.TimeColumnBuilder; import org.apache.iotdb.tsfile.read.filter.basic.Filter; import com.google.common.util.concurrent.ListenableFuture; @@ -45,6 +44,9 @@ import java.util.List; import java.util.Set; +import static org.apache.iotdb.db.mpp.execution.operator.process.RawDataAggregateOperator.isEndCalc; +import static org.apache.iotdb.db.mpp.execution.operator.process.RawDataAggregateOperator.skipOutOfTimeRangePoints; + /** * This operator is responsible to do the aggregation calculation for one series based on global * time range and time split parameter. @@ -101,7 +103,7 @@ public SeriesAggregateScanOperator( dataTypes.addAll(Arrays.asList(aggregator.getOutputType())); } tsBlockBuilder = new TsBlockBuilder(dataTypes); - this.timeRangeIterator = initTimeRangeIterator(groupByTimeParameter); + this.timeRangeIterator = initTimeRangeIterator(groupByTimeParameter, ascending); } /** @@ -109,7 +111,8 @@ public SeriesAggregateScanOperator( * Aggregation query has only one time window and the result set of it does not contain a * timestamp, so it doesn't matter what the time range returns. */ - public ITimeRangeIterator initTimeRangeIterator(GroupByTimeParameter groupByTimeParameter) { + public static ITimeRangeIterator initTimeRangeIterator( + GroupByTimeParameter groupByTimeParameter, boolean ascending) { if (groupByTimeParameter == null) { return new SingleTimeWindowIterator(0, Long.MAX_VALUE); } else { @@ -121,6 +124,7 @@ public ITimeRangeIterator initTimeRangeIterator(GroupByTimeParameter groupByTime ascending, groupByTimeParameter.isIntervalByMonth(), groupByTimeParameter.isSlidingStepByMonth(), + groupByTimeParameter.isLeftCRightO(), groupByTimeParameter.getInterval() > groupByTimeParameter.getSlidingStep()); } } @@ -164,28 +168,28 @@ public boolean hasNext() { // 2. Calculate aggregation result based on current time window if (calcFromCacheData(curTimeRange)) { - updateResultTsBlockUsingAggregateResult(); + updateResultTsBlockFromAggregators(); return true; } // read page data firstly if (readAndCalcFromPage(curTimeRange)) { - updateResultTsBlockUsingAggregateResult(); + updateResultTsBlockFromAggregators(); return true; } // read chunk data secondly if (readAndCalcFromChunk(curTimeRange)) { - updateResultTsBlockUsingAggregateResult(); + updateResultTsBlockFromAggregators(); return true; } // read from file first while (seriesScanUtil.hasNextFile()) { Statistics fileStatistics = seriesScanUtil.currentFileStatistics(); - if (fileStatistics.getStartTime() >= curTimeRange.getMax()) { + if (fileStatistics.getStartTime() > curTimeRange.getMax()) { if (ascending) { - updateResultTsBlockUsingAggregateResult(); + updateResultTsBlockFromAggregators(); return true; } else { seriesScanUtil.skipCurrentFile(); @@ -202,35 +206,22 @@ public boolean hasNext() { // read chunk if (readAndCalcFromChunk(curTimeRange)) { - updateResultTsBlockUsingAggregateResult(); + updateResultTsBlockFromAggregators(); return true; } } - updateResultTsBlockUsingAggregateResult(); + updateResultTsBlockFromAggregators(); return true; } catch (IOException e) { throw new RuntimeException("Error while scanning the file", e); } } - private void updateResultTsBlockUsingAggregateResult() { - tsBlockBuilder.reset(); - TimeColumnBuilder timeColumnBuilder = tsBlockBuilder.getTimeColumnBuilder(); - // Use start time of current time range as time column - timeColumnBuilder.writeLong(curTimeRange.getMin()); - ColumnBuilder[] columnBuilders = tsBlockBuilder.getValueColumnBuilders(); - int columnIndex = 0; - for (Aggregator aggregator : aggregators) { - ColumnBuilder[] columnBuilder = new ColumnBuilder[aggregator.getOutputType().length]; - columnBuilder[0] = columnBuilders[columnIndex++]; - if (columnBuilder.length > 1) { - columnBuilder[1] = columnBuilders[columnIndex++]; - } - aggregator.outputResult(columnBuilder); - } - tsBlockBuilder.declarePosition(); - resultTsBlock = tsBlockBuilder.build(); + private void updateResultTsBlockFromAggregators() { + resultTsBlock = + AggregateOperator.updateResultTsBlockFromAggregators( + tsBlockBuilder, aggregators, timeRangeIterator); hasCachedTsBlock = true; } @@ -242,7 +233,7 @@ public void close() throws Exception { @Override public boolean isFinished() { - return finished || (finished = hasNext()); + return finished || (finished = !hasNext()); } @Override @@ -261,20 +252,20 @@ private boolean calcFromCacheData(TimeRange curTimeRange) throws IOException { // The result is calculated from the cache return (preCachedData != null && (ascending - ? preCachedData.getEndTime() >= curTimeRange.getMax() - : preCachedData.getStartTime() < curTimeRange.getMin())) - || isEndCalc(); + ? preCachedData.getEndTime() > curTimeRange.getMax() + : preCachedData.getEndTime() < curTimeRange.getMin())) + || isEndCalc(aggregators); } @SuppressWarnings("squid:S3776") private void calcFromBatch(TsBlock tsBlock, TimeRange curTimeRange) { // check if the batchData does not contain points in current interval - if (tsBlock == null || !satisfied(tsBlock, curTimeRange)) { + if (tsBlock == null || !satisfied(tsBlock, curTimeRange, ascending)) { return; } // skip points that cannot be calculated - tsBlock = skipOutOfTimeRangePoints(tsBlock, curTimeRange); + tsBlock = skipOutOfTimeRangePoints(tsBlock, curTimeRange, ascending); for (Aggregator aggregator : aggregators) { // current agg method has been calculated @@ -291,22 +282,7 @@ private void calcFromBatch(TsBlock tsBlock, TimeRange curTimeRange) { } } - // skip points that cannot be calculated - private TsBlock skipOutOfTimeRangePoints(TsBlock tsBlock, TimeRange curTimeRange) { - TsBlockSingleColumnIterator tsBlockIterator = tsBlock.getTsBlockSingleColumnIterator(); - if (ascending) { - while (tsBlockIterator.hasNext() && tsBlockIterator.currentTime() < curTimeRange.getMin()) { - tsBlockIterator.next(); - } - } else { - while (tsBlockIterator.hasNext() && tsBlockIterator.currentTime() >= curTimeRange.getMax()) { - tsBlockIterator.next(); - } - } - return tsBlock.subTsBlock(tsBlockIterator.getRowIndex()); - } - - private boolean satisfied(TsBlock tsBlock, TimeRange timeRange) { + private boolean satisfied(TsBlock tsBlock, TimeRange timeRange, boolean ascending) { TsBlockSingleColumnIterator tsBlockIterator = tsBlock.getTsBlockSingleColumnIterator(); if (tsBlockIterator == null || !tsBlockIterator.hasNext()) { return false; @@ -314,11 +290,11 @@ private boolean satisfied(TsBlock tsBlock, TimeRange timeRange) { if (ascending && (tsBlockIterator.getEndTime() < timeRange.getMin() - || tsBlockIterator.currentTime() >= timeRange.getMax())) { + || tsBlockIterator.currentTime() > timeRange.getMax())) { return false; } if (!ascending - && (tsBlockIterator.getStartTime() >= timeRange.getMax() + && (tsBlockIterator.getEndTime() > timeRange.getMax() || tsBlockIterator.currentTime() < timeRange.getMin())) { preCachedData = tsBlock; return false; @@ -326,15 +302,6 @@ private boolean satisfied(TsBlock tsBlock, TimeRange timeRange) { return true; } - private boolean isEndCalc() { - for (Aggregator aggregator : aggregators) { - if (!aggregator.hasFinalResult()) { - return false; - } - } - return true; - } - @SuppressWarnings("squid:S3776") // Suppress high Cognitive Complexity warning private boolean readAndCalcFromPage(TimeRange curTimeRange) throws IOException { while (seriesScanUtil.hasNextPage()) { @@ -342,7 +309,7 @@ private boolean readAndCalcFromPage(TimeRange curTimeRange) throws IOException { // must be non overlapped page if (pageStatistics != null) { // There is no more eligible points in current time range - if (pageStatistics.getStartTime() >= curTimeRange.getMax()) { + if (pageStatistics.getStartTime() > curTimeRange.getMax()) { if (ascending) { return true; } else { @@ -355,7 +322,7 @@ private boolean readAndCalcFromPage(TimeRange curTimeRange) throws IOException { && curTimeRange.contains(pageStatistics.getStartTime(), pageStatistics.getEndTime())) { calcFromStatistics(pageStatistics); seriesScanUtil.skipCurrentPage(); - if (isEndCalc()) { + if (isEndCalc(aggregators)) { return true; } continue; @@ -373,7 +340,7 @@ private boolean readAndCalcFromPage(TimeRange curTimeRange) throws IOException { // lastReadIndex = tsBlockIterator.getRowIndex(); // stop calc and cached current batchData - if (ascending && tsBlockIterator.currentTime() >= curTimeRange.getMax()) { + if (ascending && tsBlockIterator.currentTime() > curTimeRange.getMax()) { preCachedData = tsBlock; return true; } @@ -382,10 +349,10 @@ private boolean readAndCalcFromPage(TimeRange curTimeRange) throws IOException { calcFromBatch(tsBlock, curTimeRange); // judge whether the calculation finished - if (isEndCalc() + if (isEndCalc(aggregators) || (tsBlockIterator.hasNext() && (ascending - ? tsBlockIterator.currentTime() >= curTimeRange.getMax() + ? tsBlockIterator.currentTime() > curTimeRange.getMax() : tsBlockIterator.currentTime() < curTimeRange.getMin()))) { return true; } @@ -396,7 +363,7 @@ private boolean readAndCalcFromPage(TimeRange curTimeRange) throws IOException { private boolean readAndCalcFromChunk(TimeRange curTimeRange) throws IOException { while (seriesScanUtil.hasNextChunk()) { Statistics chunkStatistics = seriesScanUtil.currentChunkStatistics(); - if (chunkStatistics.getStartTime() >= curTimeRange.getMax()) { + if (chunkStatistics.getStartTime() > curTimeRange.getMax()) { if (ascending) { return true; } else { diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/schedule/DriverScheduler.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/schedule/DriverScheduler.java index 9d823081381c..b1a5cc66fb30 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/execution/schedule/DriverScheduler.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/schedule/DriverScheduler.java @@ -66,7 +66,7 @@ public static DriverScheduler getInstance() { private static final int MAX_CAPACITY = 1000; // TODO: load from config files private static final int WORKER_THREAD_NUM = 4; // TODO: load from config files - private static final int QUERY_TIMEOUT_MS = 10_000; // TODO: load from config files or requests + private static final int QUERY_TIMEOUT_MS = 60_000; // TODO: load from config files or requests private final ThreadGroup workerGroups; private final List threads; diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/DistributionPlanner.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/DistributionPlanner.java index 775601c0b6b6..f0eb1fb38736 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/DistributionPlanner.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/DistributionPlanner.java @@ -39,11 +39,14 @@ import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.SchemaFetchScanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.SchemaQueryMergeNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.SchemaQueryScanNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.process.AggregationNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.process.ExchangeNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.process.TimeJoinNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.sink.FragmentSinkNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.source.SeriesAggregationScanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.source.SeriesScanNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.AggregationDescriptor; +import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.AggregationStep; import org.apache.iotdb.db.mpp.plan.statement.crud.QueryStatement; import java.util.ArrayList; @@ -231,6 +234,51 @@ public PlanNode visitSeriesScan(SeriesScanNode node, DistributionPlanContext con return timeJoinNode; } + @Override + public PlanNode visitSeriesAggregationScan( + SeriesAggregationScanNode node, DistributionPlanContext context) { + List dataDistribution = + analysis.getPartitionInfo(node.getSeriesPath(), node.getTimeFilter()); + if (dataDistribution.size() == 1) { + node.setRegionReplicaSet(dataDistribution.get(0)); + return node; + } + + List leafAggDescriptorList = new ArrayList<>(); + node.getAggregationDescriptorList() + .forEach( + descriptor -> { + leafAggDescriptorList.add( + new AggregationDescriptor( + descriptor.getAggregationType(), + AggregationStep.PARTIAL, + descriptor.getInputExpressions())); + }); + + List rootAggDescriptorList = new ArrayList<>(); + node.getAggregationDescriptorList() + .forEach( + descriptor -> { + rootAggDescriptorList.add( + new AggregationDescriptor( + descriptor.getAggregationType(), + AggregationStep.FINAL, + descriptor.getInputExpressions())); + }); + + AggregationNode aggregationNode = + new AggregationNode( + context.queryContext.getQueryId().genPlanNodeId(), rootAggDescriptorList); + for (TRegionReplicaSet dataRegion : dataDistribution) { + SeriesAggregationScanNode split = (SeriesAggregationScanNode) node.clone(); + split.setAggregationDescriptorList(leafAggDescriptorList); + split.setPlanNodeId(context.queryContext.getQueryId().genPlanNodeId()); + split.setRegionReplicaSet(dataRegion); + aggregationNode.addChild(split); + } + return aggregationNode; + } + @Override public PlanNode visitSchemaFetchMerge( SchemaFetchMergeNode node, DistributionPlanContext context) { @@ -433,7 +481,8 @@ public PlanNode visitSeriesScan(SeriesScanNode node, NodeGroupContext context) { } @Override - public PlanNode visitSeriesAggregate(SeriesAggregationScanNode node, NodeGroupContext context) { + public PlanNode visitSeriesAggregationScan( + SeriesAggregationScanNode node, NodeGroupContext context) { context.putNodeDistribution( node.getPlanNodeId(), new NodeDistribution(NodeDistributionType.NO_CHILD, node.getRegionReplicaSet())); diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LocalExecutionPlanner.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LocalExecutionPlanner.java index 6a8c5956e218..da95d2b075d9 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LocalExecutionPlanner.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LocalExecutionPlanner.java @@ -37,11 +37,13 @@ import org.apache.iotdb.db.mpp.execution.fragment.FragmentInstanceContext; import org.apache.iotdb.db.mpp.execution.operator.Operator; import org.apache.iotdb.db.mpp.execution.operator.OperatorContext; +import org.apache.iotdb.db.mpp.execution.operator.process.AggregateOperator; import org.apache.iotdb.db.mpp.execution.operator.process.DeviceMergeOperator; import org.apache.iotdb.db.mpp.execution.operator.process.DeviceViewOperator; import org.apache.iotdb.db.mpp.execution.operator.process.FilterOperator; import org.apache.iotdb.db.mpp.execution.operator.process.LimitOperator; import org.apache.iotdb.db.mpp.execution.operator.process.OffsetOperator; +import org.apache.iotdb.db.mpp.execution.operator.process.RawDataAggregateOperator; import org.apache.iotdb.db.mpp.execution.operator.process.TimeJoinOperator; import org.apache.iotdb.db.mpp.execution.operator.process.TransformOperator; import org.apache.iotdb.db.mpp.execution.operator.process.merge.AscTimeComparator; @@ -95,6 +97,7 @@ import org.apache.iotdb.db.mpp.plan.planner.plan.node.source.AlignedSeriesScanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.source.SeriesAggregationScanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.source.SeriesScanNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.AggregationDescriptor; import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.InputLocation; import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.OutputColumn; import org.apache.iotdb.db.mpp.plan.statement.component.OrderBy; @@ -356,7 +359,7 @@ public Operator visitLevelTimeSeriesCount( } @Override - public Operator visitSeriesAggregate( + public Operator visitSeriesAggregationScan( SeriesAggregationScanNode node, LocalExecutionPlanContext context) { PartialPath seriesPath = node.getSeriesPath(); boolean ascending = node.getScanOrder() == OrderBy.TIMESTAMP_ASC; @@ -370,10 +373,13 @@ public Operator visitSeriesAggregate( node.getAggregationDescriptorList() .forEach( o -> - new Aggregator( - AccumulatorFactory.createAccumulator( - o.getAggregationType(), node.getSeriesPath().getSeriesType(), ascending), - o.getStep())); + aggregators.add( + new Aggregator( + AccumulatorFactory.createAccumulator( + o.getAggregationType(), + node.getSeriesPath().getSeriesType(), + ascending), + o.getStep()))); SeriesAggregateScanOperator aggregateScanOperator = new SeriesAggregateScanOperator( node.getPlanNodeId(), @@ -534,7 +540,64 @@ public Operator visitOffset(OffsetNode node, LocalExecutionPlanContext context) @Override public Operator visitRowBasedSeriesAggregate( AggregationNode node, LocalExecutionPlanContext context) { - return super.visitRowBasedSeriesAggregate(node, context); + checkArgument( + node.getAggregationDescriptorList().size() >= 1, + "Aggregation descriptorList cannot be empty"); + OperatorContext operatorContext = + context.instanceContext.addOperatorContext( + context.getNextOperatorId(), + node.getPlanNodeId(), + DeviceViewNode.class.getSimpleName()); + List children = + node.getChildren().stream() + .map(child -> child.accept(this, context)) + .collect(Collectors.toList()); + boolean ascending = node.getScanOrder() == OrderBy.TIMESTAMP_ASC; + List aggregators = new ArrayList<>(); + Map> layout = makeLayout(node); + for (AggregationDescriptor descriptor : node.getAggregationDescriptorList()) { + List outputColumnNames = descriptor.getOutputColumnNames(); + // it may include double parts + List> inputLocationParts = new ArrayList<>(outputColumnNames.size()); + outputColumnNames.forEach(o -> inputLocationParts.add(layout.get(o))); + + List inputLocationList = new ArrayList<>(); + for (int i = 0; i < inputLocationParts.get(0).size(); i++) { + if (outputColumnNames.size() == 1) { + inputLocationList.add(new InputLocation[] {inputLocationParts.get(0).get(i)}); + } else { + inputLocationList.add( + new InputLocation[] { + inputLocationParts.get(0).get(i), inputLocationParts.get(1).get(i) + }); + } + } + + aggregators.add( + new Aggregator( + AccumulatorFactory.createAccumulator( + descriptor.getAggregationType(), + context + .getTypeProvider() + // get the type of first inputExpression + .getType(descriptor.getInputExpressions().get(0).toString()), + ascending), + descriptor.getStep(), + inputLocationList)); + } + boolean inputRaw = node.getAggregationDescriptorList().get(0).getStep().isInputRaw(); + if (inputRaw) { + checkArgument(children.size() == 1, "rawDataAggregateOperator can only accept one input"); + return new RawDataAggregateOperator( + operatorContext, + aggregators, + children.get(0), + ascending, + node.getGroupByTimeParameter()); + } else { + return new AggregateOperator( + operatorContext, aggregators, children, ascending, node.getGroupByTimeParameter()); + } } @Override diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/PlanVisitor.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/PlanVisitor.java index b11dac5a2668..61470a7f2026 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/PlanVisitor.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/PlanVisitor.java @@ -65,7 +65,7 @@ public R visitSeriesScan(SeriesScanNode node, C context) { return visitPlan(node, context); } - public R visitSeriesAggregate(SeriesAggregationScanNode node, C context) { + public R visitSeriesAggregationScan(SeriesAggregationScanNode node, C context) { return visitPlan(node, context); } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/process/AggregationNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/process/AggregationNode.java index 7693abf79d10..e02958ea62a7 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/process/AggregationNode.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/process/AggregationNode.java @@ -24,13 +24,16 @@ import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanVisitor; import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.AggregationDescriptor; import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.GroupByTimeParameter; +import org.apache.iotdb.db.mpp.plan.statement.component.OrderBy; import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils; import javax.annotation.Nullable; import java.nio.ByteBuffer; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; @@ -43,11 +46,12 @@ public class AggregationNode extends ProcessNode { // The list of aggregate functions, each AggregateDescriptor will be output as one column of // result TsBlock - protected final List aggregationDescriptorList; + protected List aggregationDescriptorList; // The parameter of `group by time`. // Its value will be null if there is no `group by time` clause. @Nullable protected GroupByTimeParameter groupByTimeParameter; + protected OrderBy scanOrder = OrderBy.TIMESTAMP_ASC; protected List children; @@ -55,9 +59,7 @@ public AggregationNode( PlanNodeId id, List children, List aggregationDescriptorList) { - super(id); - this.children = children; - this.aggregationDescriptorList = aggregationDescriptorList; + this(id, children, aggregationDescriptorList, null); } public AggregationNode( @@ -67,14 +69,12 @@ public AggregationNode( @Nullable GroupByTimeParameter groupByTimeParameter) { super(id); this.children = children; - this.aggregationDescriptorList = aggregationDescriptorList; + this.aggregationDescriptorList = getDeduplicatedDescriptors(aggregationDescriptorList); this.groupByTimeParameter = groupByTimeParameter; } public AggregationNode(PlanNodeId id, List aggregationDescriptorList) { - super(id); - this.aggregationDescriptorList = aggregationDescriptorList; - this.children = new ArrayList<>(); + this(id, aggregationDescriptorList, null); } public AggregationNode( @@ -82,7 +82,7 @@ public AggregationNode( List aggregationDescriptorList, @Nullable GroupByTimeParameter groupByTimeParameter) { super(id); - this.aggregationDescriptorList = aggregationDescriptorList; + this.aggregationDescriptorList = getDeduplicatedDescriptors(aggregationDescriptorList); this.groupByTimeParameter = groupByTimeParameter; this.children = new ArrayList<>(); } @@ -96,6 +96,10 @@ public GroupByTimeParameter getGroupByTimeParameter() { return groupByTimeParameter; } + public OrderBy getScanOrder() { + return scanOrder; + } + @Override public List getChildren() { return children; @@ -183,4 +187,46 @@ public int hashCode() { return Objects.hash( super.hashCode(), aggregationDescriptorList, groupByTimeParameter, children); } + + /** + * If aggregation function COUNT and AVG for one time series appears at the same time, and outputs + * intermediate result, the output columns will be like | COUNT | COUNT | SUM |. In this + * situation, one COUNT column is not needed. Therefore, when COUNT(or SUM) appears with AVG and + * outputs intermediate result(if they output final result, they will be all necessary), we need + * to REMOVE the COUNT aggregation, and only keep AVG function no matter their appearing order. + * + *

The related functions include AVG(COUNT AND SUM), FIRST_VALUE(FIRST_VALUE AND MIN_TIME), + * LAST_VALUE(LAST_VALUE AND MAX_TIME). + */ + public static List getDeduplicatedDescriptors( + List aggregationDescriptors) { + Map columnToIndexMap = new HashMap<>(); + boolean[] removedIndexes = new boolean[aggregationDescriptors.size()]; + for (int i = 0; i < aggregationDescriptors.size(); i++) { + AggregationDescriptor descriptor = aggregationDescriptors.get(i); + if (descriptor.getStep().isOutputPartial()) { + List outputColumnNames = descriptor.getOutputColumnNames(); + for (String outputColumn : outputColumnNames) { + // if encountering repeated column + if (columnToIndexMap.containsKey(outputColumn)) { + // if self is double outputs, then remove the former, else remove self + if (outputColumnNames.size() == 2) { + removedIndexes[columnToIndexMap.get(outputColumn)] = true; + } else { + removedIndexes[i] = true; + } + } else { + columnToIndexMap.put(outputColumn, i); + } + } + } + } + List deduplicatedDescriptors = new ArrayList<>(); + for (int i = 0; i < aggregationDescriptors.size(); i++) { + if (!removedIndexes[i]) { + deduplicatedDescriptors.add(aggregationDescriptors.get(i)); + } + } + return deduplicatedDescriptors; + } } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/process/GroupByLevelNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/process/GroupByLevelNode.java index e072d6d3f767..034456c692a0 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/process/GroupByLevelNode.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/process/GroupByLevelNode.java @@ -30,7 +30,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; -import java.util.stream.Collectors; /** * This node is responsible for the final aggregation merge operation. It will process the data from @@ -97,10 +96,7 @@ public PlanNode clone() { @Override public List getOutputColumnNames() { - return aggregationDescriptorList.stream() - .map(AggregationDescriptor::getOutputColumnNames) - .flatMap(List::stream) - .collect(Collectors.toList()); + return outputColumnNames; } @Override diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/source/SeriesAggregationScanNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/source/SeriesAggregationScanNode.java index abf3e5b8d9f9..ced35c4fb43c 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/source/SeriesAggregationScanNode.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/source/SeriesAggregationScanNode.java @@ -26,6 +26,7 @@ import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeType; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanVisitor; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.process.AggregationNode; import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.AggregationDescriptor; import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.GroupByTimeParameter; import org.apache.iotdb.db.mpp.plan.statement.component.OrderBy; @@ -65,7 +66,7 @@ public class SeriesAggregationScanNode extends SourceNode { // The list of aggregate functions, each AggregateDescriptor will be output as one column in // result TsBlock - private final List aggregationDescriptorList; + private List aggregationDescriptorList; // The order to traverse the data. // Currently, we only support TIMESTAMP_ASC and TIMESTAMP_DESC here. @@ -88,7 +89,8 @@ public SeriesAggregationScanNode( List aggregationDescriptorList) { super(id); this.seriesPath = seriesPath; - this.aggregationDescriptorList = aggregationDescriptorList; + this.aggregationDescriptorList = + AggregationNode.getDeduplicatedDescriptors(aggregationDescriptorList); } public SeriesAggregationScanNode( @@ -176,6 +178,10 @@ public List getOutputColumnNames() { .collect(Collectors.toList()); } + public void setAggregationDescriptorList(List aggregationDescriptorList) { + this.aggregationDescriptorList = aggregationDescriptorList; + } + @Override public void open() throws Exception {} @@ -194,7 +200,7 @@ public void close() throws Exception {} @Override public R accept(PlanVisitor visitor, C context) { - return visitor.visitSeriesAggregate(this, context); + return visitor.visitSeriesAggregationScan(this, context); } @Override diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/parameter/AggregationDescriptor.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/parameter/AggregationDescriptor.java index 1f1923a6b252..74fd995005af 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/parameter/AggregationDescriptor.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/parameter/AggregationDescriptor.java @@ -44,6 +44,8 @@ public class AggregationDescriptor { */ private final List inputExpressions; + private String parametersString; + public AggregationDescriptor( AggregationType aggregationType, AggregationStep step, List inputExpressions) { this.aggregationType = aggregationType; @@ -52,7 +54,60 @@ public AggregationDescriptor( } public List getOutputColumnNames() { - return new ArrayList<>(); + List outputAggregationTypes = new ArrayList<>(); + if (step.isOutputPartial()) { + switch (aggregationType) { + case AVG: + outputAggregationTypes.add(AggregationType.COUNT); + outputAggregationTypes.add(AggregationType.SUM); + break; + case FIRST_VALUE: + outputAggregationTypes.add(AggregationType.FIRST_VALUE); + outputAggregationTypes.add(AggregationType.MIN_TIME); + break; + case LAST_VALUE: + outputAggregationTypes.add(AggregationType.LAST_VALUE); + outputAggregationTypes.add(AggregationType.MAX_TIME); + break; + default: + outputAggregationTypes.add(aggregationType); + } + } else { + outputAggregationTypes.add(aggregationType); + } + List outputColumnNames = new ArrayList<>(); + for (AggregationType outputType : outputAggregationTypes) { + outputColumnNames.add( + outputType.toString().toLowerCase() + "(" + getParametersString() + ")"); + } + return outputColumnNames; + } + + /** + * Generates the parameter part of the function column name. + * + *

Example: + * + *

Full column name -> udf(root.sg.d.s1, sin(root.sg.d.s1)) + * + *

The parameter part -> root.sg.d.s1, sin(root.sg.d.s1) + */ + public String getParametersString() { + if (parametersString == null) { + StringBuilder builder = new StringBuilder(); + if (!inputExpressions.isEmpty()) { + builder.append(inputExpressions.get(0).toString()); + for (int i = 1; i < inputExpressions.size(); ++i) { + builder.append(", ").append(inputExpressions.get(i).toString()); + } + } + parametersString = builder.toString(); + } + return parametersString; + } + + public List getInputExpressions() { + return inputExpressions; } public AggregationType getAggregationType() { @@ -67,10 +122,6 @@ public void setStep(AggregationStep step) { this.step = step; } - public List getInputExpressions() { - return inputExpressions; - } - public void serialize(ByteBuffer byteBuffer) { ReadWriteIOUtils.write(aggregationType.ordinal(), byteBuffer); step.serialize(byteBuffer); diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/parameter/AggregationStep.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/parameter/AggregationStep.java index 8eab8d0f621e..3c3af3cd8358 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/parameter/AggregationStep.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/parameter/AggregationStep.java @@ -31,48 +31,46 @@ *

    *
  • Raw: raw data, as input only *
  • Partial: intermediate aggregation result - *
  • Final: final aggregation result, as output only + *
  • Final: final aggregation result *
*/ public enum AggregationStep { // input Raw, output Partial - PARTIAL(true, true), + PARTIAL(InputType.RAW, true), // input Partial, output Final - FINAL(false, false), + FINAL(InputType.PARTIAL, false), // input Partial, output Partial - INTERMEDIATE(false, true), + INTERMEDIATE(InputType.PARTIAL, true), // input Raw, output Final - SINGLE(true, false); + SINGLE(InputType.RAW, false), + // input final, output final + STATIC(InputType.FINAL, false); - private final boolean inputRaw; + private enum InputType { + RAW, + PARTIAL, + FINAL + } + + private final InputType inputType; private final boolean outputPartial; - AggregationStep(boolean inputRaw, boolean outputPartial) { - this.inputRaw = inputRaw; + AggregationStep(InputType inputType, boolean outputPartial) { + this.inputType = inputType; this.outputPartial = outputPartial; } public boolean isInputRaw() { - return inputRaw; + return inputType == InputType.RAW; } - public boolean isOutputPartial() { - return outputPartial; + public boolean isInputFinal() { + return inputType == InputType.FINAL; } - public static AggregationStep partialOutput(AggregationStep step) { - if (step.isInputRaw()) { - return AggregationStep.PARTIAL; - } - return AggregationStep.INTERMEDIATE; - } - - public static AggregationStep partialInput(AggregationStep step) { - if (step.isOutputPartial()) { - return AggregationStep.INTERMEDIATE; - } - return AggregationStep.FINAL; + public boolean isOutputPartial() { + return outputPartial; } public void serialize(ByteBuffer byteBuffer) { diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/Expression.java b/server/src/main/java/org/apache/iotdb/db/query/expression/Expression.java index 28698ab1335b..ee460b61b401 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/Expression.java +++ b/server/src/main/java/org/apache/iotdb/db/query/expression/Expression.java @@ -71,7 +71,7 @@ public abstract class Expression { ///////////////////////////////////////////////////////////////////////////////////////////////// - // Expression type inferring for execution plan generation + // Expression type inferring for execution plan generation ////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////// public boolean isBuiltInAggregationFunctionExpression() { diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/aggregation/AccumulatorTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/aggregation/AccumulatorTest.java index 6fe08cd34523..f329609a293a 100644 --- a/server/src/test/java/org/apache/iotdb/db/mpp/aggregation/AccumulatorTest.java +++ b/server/src/test/java/org/apache/iotdb/db/mpp/aggregation/AccumulatorTest.java @@ -73,10 +73,19 @@ public void avgAccumulatorTest() { Assert.assertEquals(TSDataType.INT64, avgAccumulator.getIntermediateType()[0]); Assert.assertEquals(TSDataType.DOUBLE, avgAccumulator.getIntermediateType()[1]); Assert.assertEquals(TSDataType.DOUBLE, avgAccumulator.getFinalType()); + // check returning null while no data + ColumnBuilder[] intermediateResult = new ColumnBuilder[2]; + intermediateResult[0] = new LongColumnBuilder(null, 1); + intermediateResult[1] = new DoubleColumnBuilder(null, 1); + avgAccumulator.outputIntermediate(intermediateResult); + Assert.assertTrue(intermediateResult[0].build().isNull(0)); + Assert.assertTrue(intermediateResult[1].build().isNull(0)); + ColumnBuilder finalResult = new DoubleColumnBuilder(null, 1); + avgAccumulator.outputFinal(finalResult); + Assert.assertTrue(finalResult.build().isNull(0)); avgAccumulator.addInput(rawData.getTimeAndValueColumn(0), defaultTimeRange); Assert.assertFalse(avgAccumulator.hasFinalResult()); - ColumnBuilder[] intermediateResult = new ColumnBuilder[2]; intermediateResult[0] = new LongColumnBuilder(null, 1); intermediateResult[1] = new DoubleColumnBuilder(null, 1); avgAccumulator.outputIntermediate(intermediateResult); @@ -86,7 +95,7 @@ public void avgAccumulatorTest() { // add intermediate result as input avgAccumulator.addIntermediate( new Column[] {intermediateResult[0].build(), intermediateResult[1].build()}); - ColumnBuilder finalResult = new DoubleColumnBuilder(null, 1); + finalResult = new DoubleColumnBuilder(null, 1); avgAccumulator.outputFinal(finalResult); Assert.assertEquals(49.5d, finalResult.build().getDouble(0), 0.001); @@ -103,17 +112,24 @@ public void countAccumulatorTest() { AccumulatorFactory.createAccumulator(AggregationType.COUNT, TSDataType.DOUBLE, true); Assert.assertEquals(TSDataType.INT64, countAccumulator.getIntermediateType()[0]); Assert.assertEquals(TSDataType.INT64, countAccumulator.getFinalType()); + // check returning null while no data + ColumnBuilder[] intermediateResult = new ColumnBuilder[1]; + intermediateResult[0] = new LongColumnBuilder(null, 1); + countAccumulator.outputIntermediate(intermediateResult); + Assert.assertEquals(0, intermediateResult[0].build().getLong(0)); + ColumnBuilder finalResult = new LongColumnBuilder(null, 1); + countAccumulator.outputFinal(finalResult); + Assert.assertEquals(0, finalResult.build().getLong(0)); countAccumulator.addInput(rawData.getTimeAndValueColumn(0), defaultTimeRange); Assert.assertFalse(countAccumulator.hasFinalResult()); - ColumnBuilder[] intermediateResult = new ColumnBuilder[1]; intermediateResult[0] = new LongColumnBuilder(null, 1); countAccumulator.outputIntermediate(intermediateResult); Assert.assertEquals(100, intermediateResult[0].build().getLong(0)); // add intermediate result as input countAccumulator.addIntermediate(new Column[] {intermediateResult[0].build()}); - ColumnBuilder finalResult = new LongColumnBuilder(null, 1); + finalResult = new LongColumnBuilder(null, 1); countAccumulator.outputFinal(finalResult); Assert.assertEquals(200, finalResult.build().getLong(0)); @@ -130,17 +146,24 @@ public void extremeAccumulatorTest() { AccumulatorFactory.createAccumulator(AggregationType.EXTREME, TSDataType.DOUBLE, true); Assert.assertEquals(TSDataType.DOUBLE, extremeAccumulator.getIntermediateType()[0]); Assert.assertEquals(TSDataType.DOUBLE, extremeAccumulator.getFinalType()); + // check returning null while no data + ColumnBuilder[] intermediateResult = new ColumnBuilder[1]; + intermediateResult[0] = new DoubleColumnBuilder(null, 1); + extremeAccumulator.outputIntermediate(intermediateResult); + Assert.assertTrue(intermediateResult[0].build().isNull(0)); + ColumnBuilder finalResult = new DoubleColumnBuilder(null, 1); + extremeAccumulator.outputFinal(finalResult); + Assert.assertTrue(finalResult.build().isNull(0)); extremeAccumulator.addInput(rawData.getTimeAndValueColumn(0), defaultTimeRange); Assert.assertFalse(extremeAccumulator.hasFinalResult()); - ColumnBuilder[] intermediateResult = new ColumnBuilder[1]; intermediateResult[0] = new DoubleColumnBuilder(null, 1); extremeAccumulator.outputIntermediate(intermediateResult); Assert.assertEquals(99d, intermediateResult[0].build().getDouble(0), 0.001); // add intermediate result as input extremeAccumulator.addIntermediate(new Column[] {intermediateResult[0].build()}); - ColumnBuilder finalResult = new DoubleColumnBuilder(null, 1); + finalResult = new DoubleColumnBuilder(null, 1); extremeAccumulator.outputFinal(finalResult); Assert.assertEquals(99d, finalResult.build().getDouble(0), 0.001); @@ -158,10 +181,19 @@ public void firstValueAccumulatorTest() { Assert.assertEquals(TSDataType.DOUBLE, firstValueAccumulator.getIntermediateType()[0]); Assert.assertEquals(TSDataType.INT64, firstValueAccumulator.getIntermediateType()[1]); Assert.assertEquals(TSDataType.DOUBLE, firstValueAccumulator.getFinalType()); + // check returning null while no data + ColumnBuilder[] intermediateResult = new ColumnBuilder[2]; + intermediateResult[0] = new DoubleColumnBuilder(null, 1); + intermediateResult[1] = new LongColumnBuilder(null, 1); + firstValueAccumulator.outputIntermediate(intermediateResult); + Assert.assertTrue(intermediateResult[0].build().isNull(0)); + Assert.assertTrue(intermediateResult[1].build().isNull(0)); + ColumnBuilder finalResult = new DoubleColumnBuilder(null, 1); + firstValueAccumulator.outputFinal(finalResult); + Assert.assertTrue(finalResult.build().isNull(0)); firstValueAccumulator.addInput(rawData.getTimeAndValueColumn(0), defaultTimeRange); Assert.assertTrue(firstValueAccumulator.hasFinalResult()); - ColumnBuilder[] intermediateResult = new ColumnBuilder[2]; intermediateResult[0] = new DoubleColumnBuilder(null, 1); intermediateResult[1] = new LongColumnBuilder(null, 1); firstValueAccumulator.outputIntermediate(intermediateResult); @@ -171,7 +203,7 @@ public void firstValueAccumulatorTest() { // add intermediate result as input firstValueAccumulator.addIntermediate( new Column[] {intermediateResult[0].build(), intermediateResult[1].build()}); - ColumnBuilder finalResult = new DoubleColumnBuilder(null, 1); + finalResult = new DoubleColumnBuilder(null, 1); firstValueAccumulator.outputFinal(finalResult); Assert.assertEquals(0L, finalResult.build().getDouble(0), 0.001); @@ -189,9 +221,18 @@ public void lastValueAccumulatorTest() { Assert.assertEquals(TSDataType.DOUBLE, lastValueAccumulator.getIntermediateType()[0]); Assert.assertEquals(TSDataType.INT64, lastValueAccumulator.getIntermediateType()[1]); Assert.assertEquals(TSDataType.DOUBLE, lastValueAccumulator.getFinalType()); + // check returning null while no data + ColumnBuilder[] intermediateResult = new ColumnBuilder[2]; + intermediateResult[0] = new DoubleColumnBuilder(null, 1); + intermediateResult[1] = new LongColumnBuilder(null, 1); + lastValueAccumulator.outputIntermediate(intermediateResult); + Assert.assertTrue(intermediateResult[0].build().isNull(0)); + Assert.assertTrue(intermediateResult[1].build().isNull(0)); + ColumnBuilder finalResult = new DoubleColumnBuilder(null, 1); + lastValueAccumulator.outputFinal(finalResult); + Assert.assertTrue(finalResult.build().isNull(0)); lastValueAccumulator.addInput(rawData.getTimeAndValueColumn(0), defaultTimeRange); - ColumnBuilder[] intermediateResult = new ColumnBuilder[2]; intermediateResult[0] = new DoubleColumnBuilder(null, 1); intermediateResult[1] = new LongColumnBuilder(null, 1); lastValueAccumulator.outputIntermediate(intermediateResult); @@ -201,7 +242,7 @@ public void lastValueAccumulatorTest() { // add intermediate result as input lastValueAccumulator.addIntermediate( new Column[] {intermediateResult[0].build(), intermediateResult[1].build()}); - ColumnBuilder finalResult = new DoubleColumnBuilder(null, 1); + finalResult = new DoubleColumnBuilder(null, 1); lastValueAccumulator.outputFinal(finalResult); Assert.assertEquals(99L, finalResult.build().getDouble(0), 0.001); @@ -218,17 +259,24 @@ public void maxTimeAccumulatorTest() { AccumulatorFactory.createAccumulator(AggregationType.MAX_TIME, TSDataType.DOUBLE, true); Assert.assertEquals(TSDataType.INT64, maxTimeAccumulator.getIntermediateType()[0]); Assert.assertEquals(TSDataType.INT64, maxTimeAccumulator.getFinalType()); + // check returning null while no data + ColumnBuilder[] intermediateResult = new ColumnBuilder[1]; + intermediateResult[0] = new LongColumnBuilder(null, 1); + maxTimeAccumulator.outputIntermediate(intermediateResult); + Assert.assertTrue(intermediateResult[0].build().isNull(0)); + ColumnBuilder finalResult = new LongColumnBuilder(null, 1); + maxTimeAccumulator.outputFinal(finalResult); + Assert.assertTrue(finalResult.build().isNull(0)); maxTimeAccumulator.addInput(rawData.getTimeAndValueColumn(0), defaultTimeRange); Assert.assertFalse(maxTimeAccumulator.hasFinalResult()); - ColumnBuilder[] intermediateResult = new ColumnBuilder[1]; intermediateResult[0] = new LongColumnBuilder(null, 1); maxTimeAccumulator.outputIntermediate(intermediateResult); Assert.assertEquals(99, intermediateResult[0].build().getLong(0)); // add intermediate result as input maxTimeAccumulator.addIntermediate(new Column[] {intermediateResult[0].build()}); - ColumnBuilder finalResult = new LongColumnBuilder(null, 1); + finalResult = new LongColumnBuilder(null, 1); maxTimeAccumulator.outputFinal(finalResult); Assert.assertEquals(99, finalResult.build().getLong(0)); @@ -245,17 +293,24 @@ public void minTimeAccumulatorTest() { AccumulatorFactory.createAccumulator(AggregationType.MIN_TIME, TSDataType.DOUBLE, true); Assert.assertEquals(TSDataType.INT64, minTimeAccumulator.getIntermediateType()[0]); Assert.assertEquals(TSDataType.INT64, minTimeAccumulator.getFinalType()); + // check returning null while no data + ColumnBuilder[] intermediateResult = new ColumnBuilder[1]; + intermediateResult[0] = new LongColumnBuilder(null, 1); + minTimeAccumulator.outputIntermediate(intermediateResult); + Assert.assertTrue(intermediateResult[0].build().isNull(0)); + ColumnBuilder finalResult = new LongColumnBuilder(null, 1); + minTimeAccumulator.outputFinal(finalResult); + Assert.assertTrue(finalResult.build().isNull(0)); minTimeAccumulator.addInput(rawData.getTimeAndValueColumn(0), defaultTimeRange); Assert.assertTrue(minTimeAccumulator.hasFinalResult()); - ColumnBuilder[] intermediateResult = new ColumnBuilder[1]; intermediateResult[0] = new LongColumnBuilder(null, 1); minTimeAccumulator.outputIntermediate(intermediateResult); Assert.assertEquals(0, intermediateResult[0].build().getLong(0)); // add intermediate result as input minTimeAccumulator.addIntermediate(new Column[] {intermediateResult[0].build()}); - ColumnBuilder finalResult = new LongColumnBuilder(null, 1); + finalResult = new LongColumnBuilder(null, 1); minTimeAccumulator.outputFinal(finalResult); Assert.assertEquals(0, finalResult.build().getLong(0)); @@ -272,17 +327,24 @@ public void maxValueAccumulatorTest() { AccumulatorFactory.createAccumulator(AggregationType.MAX_VALUE, TSDataType.DOUBLE, true); Assert.assertEquals(TSDataType.DOUBLE, extremeAccumulator.getIntermediateType()[0]); Assert.assertEquals(TSDataType.DOUBLE, extremeAccumulator.getFinalType()); + // check returning null while no data + ColumnBuilder[] intermediateResult = new ColumnBuilder[1]; + intermediateResult[0] = new DoubleColumnBuilder(null, 1); + extremeAccumulator.outputIntermediate(intermediateResult); + Assert.assertTrue(intermediateResult[0].build().isNull(0)); + ColumnBuilder finalResult = new DoubleColumnBuilder(null, 1); + extremeAccumulator.outputFinal(finalResult); + Assert.assertTrue(finalResult.build().isNull(0)); extremeAccumulator.addInput(rawData.getTimeAndValueColumn(0), defaultTimeRange); Assert.assertFalse(extremeAccumulator.hasFinalResult()); - ColumnBuilder[] intermediateResult = new ColumnBuilder[1]; intermediateResult[0] = new DoubleColumnBuilder(null, 1); extremeAccumulator.outputIntermediate(intermediateResult); Assert.assertEquals(99d, intermediateResult[0].build().getDouble(0), 0.001); // add intermediate result as input extremeAccumulator.addIntermediate(new Column[] {intermediateResult[0].build()}); - ColumnBuilder finalResult = new DoubleColumnBuilder(null, 1); + finalResult = new DoubleColumnBuilder(null, 1); extremeAccumulator.outputFinal(finalResult); Assert.assertEquals(99d, finalResult.build().getDouble(0), 0.001); @@ -299,17 +361,24 @@ public void minValueAccumulatorTest() { AccumulatorFactory.createAccumulator(AggregationType.MIN_VALUE, TSDataType.DOUBLE, true); Assert.assertEquals(TSDataType.DOUBLE, extremeAccumulator.getIntermediateType()[0]); Assert.assertEquals(TSDataType.DOUBLE, extremeAccumulator.getFinalType()); + // check returning null while no data + ColumnBuilder[] intermediateResult = new ColumnBuilder[1]; + intermediateResult[0] = new DoubleColumnBuilder(null, 1); + extremeAccumulator.outputIntermediate(intermediateResult); + Assert.assertTrue(intermediateResult[0].build().isNull(0)); + ColumnBuilder finalResult = new DoubleColumnBuilder(null, 1); + extremeAccumulator.outputFinal(finalResult); + Assert.assertTrue(finalResult.build().isNull(0)); extremeAccumulator.addInput(rawData.getTimeAndValueColumn(0), defaultTimeRange); Assert.assertFalse(extremeAccumulator.hasFinalResult()); - ColumnBuilder[] intermediateResult = new ColumnBuilder[1]; intermediateResult[0] = new DoubleColumnBuilder(null, 1); extremeAccumulator.outputIntermediate(intermediateResult); Assert.assertEquals(0d, intermediateResult[0].build().getDouble(0), 0.001); // add intermediate result as input extremeAccumulator.addIntermediate(new Column[] {intermediateResult[0].build()}); - ColumnBuilder finalResult = new DoubleColumnBuilder(null, 1); + finalResult = new DoubleColumnBuilder(null, 1); extremeAccumulator.outputFinal(finalResult); Assert.assertEquals(0d, finalResult.build().getDouble(0), 0.001); @@ -326,17 +395,24 @@ public void sumAccumulatorTest() { AccumulatorFactory.createAccumulator(AggregationType.SUM, TSDataType.DOUBLE, true); Assert.assertEquals(TSDataType.DOUBLE, sumAccumulator.getIntermediateType()[0]); Assert.assertEquals(TSDataType.DOUBLE, sumAccumulator.getFinalType()); + // check returning null while no data + ColumnBuilder[] intermediateResult = new ColumnBuilder[1]; + intermediateResult[0] = new DoubleColumnBuilder(null, 1); + sumAccumulator.outputIntermediate(intermediateResult); + Assert.assertTrue(intermediateResult[0].build().isNull(0)); + ColumnBuilder finalResult = new DoubleColumnBuilder(null, 1); + sumAccumulator.outputFinal(finalResult); + Assert.assertTrue(finalResult.build().isNull(0)); sumAccumulator.addInput(rawData.getTimeAndValueColumn(0), defaultTimeRange); Assert.assertFalse(sumAccumulator.hasFinalResult()); - ColumnBuilder[] intermediateResult = new ColumnBuilder[1]; intermediateResult[0] = new DoubleColumnBuilder(null, 1); sumAccumulator.outputIntermediate(intermediateResult); Assert.assertEquals(4950d, intermediateResult[0].build().getDouble(0), 0.001); // add intermediate result as input sumAccumulator.addIntermediate(new Column[] {intermediateResult[0].build()}); - ColumnBuilder finalResult = new DoubleColumnBuilder(null, 1); + finalResult = new DoubleColumnBuilder(null, 1); sumAccumulator.outputFinal(finalResult); Assert.assertEquals(9900d, finalResult.build().getDouble(0), 0.001); diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/aggregation/TimeRangeIteratorTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/aggregation/TimeRangeIteratorTest.java new file mode 100644 index 000000000000..48a3f5355302 --- /dev/null +++ b/server/src/test/java/org/apache/iotdb/db/mpp/aggregation/TimeRangeIteratorTest.java @@ -0,0 +1,298 @@ +/* + * 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.iotdb.db.mpp.aggregation; + +import org.apache.iotdb.db.mpp.aggregation.timerangeiterator.ITimeRangeIterator; +import org.apache.iotdb.db.mpp.aggregation.timerangeiterator.TimeRangeIteratorFactory; +import org.apache.iotdb.tsfile.read.common.TimeRange; + +import org.junit.Assert; +import org.junit.Test; + +public class TimeRangeIteratorTest { + + @Test + public void testNotSplitTimeRange() { + String[] res = { + "[ 0 : 3 ]", + "[ 3 : 6 ]", + "[ 6 : 9 ]", + "[ 9 : 12 ]", + "[ 12 : 15 ]", + "[ 15 : 18 ]", + "[ 18 : 21 ]", + "[ 21 : 24 ]", + "[ 24 : 27 ]", + "[ 27 : 30 ]", + "[ 30 : 31 ]" + }; + + long startTime = 0, endTime = 32, interval = 4, slidingStep = 3; + + ITimeRangeIterator timeRangeIterator = + TimeRangeIteratorFactory.getTimeRangeIterator( + startTime, endTime, interval, slidingStep, true, false, false, true, false); + + checkRes(timeRangeIterator, res); + + ITimeRangeIterator descTimeRangeIterator = + TimeRangeIteratorFactory.getTimeRangeIterator( + startTime, endTime, interval, slidingStep, false, false, false, true, false); + + checkRes(descTimeRangeIterator, res); + } + + @Test + public void testSplitTimeRange() { + String[] res4_1 = { + "[ 0 : 0 ]", + "[ 1 : 1 ]", + "[ 2 : 2 ]", + "[ 3 : 3 ]", + "[ 4 : 4 ]", + "[ 5 : 5 ]", + "[ 6 : 6 ]", + "[ 7 : 7 ]", + "[ 8 : 8 ]", + "[ 9 : 9 ]", + "[ 10 : 10 ]", + "[ 11 : 11 ]", + "[ 12 : 12 ]", + "[ 13 : 13 ]", + "[ 14 : 14 ]", + "[ 15 : 15 ]", + "[ 16 : 16 ]", + "[ 17 : 17 ]", + "[ 18 : 18 ]", + "[ 19 : 19 ]", + "[ 20 : 20 ]", + "[ 21 : 21 ]", + "[ 22 : 22 ]", + "[ 23 : 23 ]", + "[ 24 : 24 ]", + "[ 25 : 25 ]", + "[ 26 : 26 ]", + "[ 27 : 27 ]", + "[ 28 : 28 ]", + "[ 29 : 29 ]", + "[ 30 : 30 ]", + "[ 31 : 31 ]" + }; + String[] res4_2 = { + "[ 0 : 1 ]", + "[ 2 : 3 ]", + "[ 4 : 5 ]", + "[ 6 : 7 ]", + "[ 8 : 9 ]", + "[ 10 : 11 ]", + "[ 12 : 13 ]", + "[ 14 : 15 ]", + "[ 16 : 17 ]", + "[ 18 : 19 ]", + "[ 20 : 21 ]", + "[ 22 : 23 ]", + "[ 24 : 25 ]", + "[ 26 : 27 ]", + "[ 28 : 29 ]", + "[ 30 : 31 ]" + }; + String[] res4_3 = { + "[ 0 : 0 ]", + "[ 1 : 2 ]", + "[ 3 : 3 ]", + "[ 4 : 5 ]", + "[ 6 : 6 ]", + "[ 7 : 8 ]", + "[ 9 : 9 ]", + "[ 10 : 11 ]", + "[ 12 : 12 ]", + "[ 13 : 14 ]", + "[ 15 : 15 ]", + "[ 16 : 17 ]", + "[ 18 : 18 ]", + "[ 19 : 20 ]", + "[ 21 : 21 ]", + "[ 22 : 23 ]", + "[ 24 : 24 ]", + "[ 25 : 26 ]", + "[ 27 : 27 ]", + "[ 28 : 29 ]", + "[ 30 : 30 ]", + "[ 31 : 31 ]" + }; + String[] res4_4 = { + "[ 0 : 3 ]", + "[ 4 : 7 ]", + "[ 8 : 11 ]", + "[ 12 : 15 ]", + "[ 16 : 19 ]", + "[ 20 : 23 ]", + "[ 24 : 27 ]", + "[ 28 : 31 ]" + }; + String[] res4_5 = { + "[ 0 : 3 ]", + "[ 5 : 8 ]", + "[ 10 : 13 ]", + "[ 15 : 18 ]", + "[ 20 : 23 ]", + "[ 25 : 28 ]", + "[ 30 : 31 ]" + }; + String[] res4_6 = { + "[ 0 : 3 ]", "[ 6 : 9 ]", "[ 12 : 15 ]", "[ 18 : 21 ]", "[ 24 : 27 ]", "[ 30 : 31 ]" + }; + + checkRes( + TimeRangeIteratorFactory.getTimeRangeIterator(0, 32, 4, 1, true, false, false, true, true), + res4_1); + checkRes( + TimeRangeIteratorFactory.getTimeRangeIterator(0, 32, 4, 2, true, false, false, true, true), + res4_2); + checkRes( + TimeRangeIteratorFactory.getTimeRangeIterator(0, 32, 4, 3, true, false, false, true, true), + res4_3); + checkRes( + TimeRangeIteratorFactory.getTimeRangeIterator(0, 32, 4, 4, true, false, false, true, true), + res4_4); + checkRes( + TimeRangeIteratorFactory.getTimeRangeIterator(0, 32, 4, 5, true, false, false, true, true), + res4_5); + checkRes( + TimeRangeIteratorFactory.getTimeRangeIterator(0, 32, 4, 6, true, false, false, true, true), + res4_6); + checkRes( + TimeRangeIteratorFactory.getTimeRangeIterator(0, 32, 4, 1, false, false, false, true, true), + res4_1); + checkRes( + TimeRangeIteratorFactory.getTimeRangeIterator(0, 32, 4, 2, false, false, false, true, true), + res4_2); + checkRes( + TimeRangeIteratorFactory.getTimeRangeIterator(0, 32, 4, 3, false, false, false, true, true), + res4_3); + checkRes( + TimeRangeIteratorFactory.getTimeRangeIterator(0, 32, 4, 4, false, false, false, true, true), + res4_4); + checkRes( + TimeRangeIteratorFactory.getTimeRangeIterator(0, 32, 4, 5, false, false, false, true, true), + res4_5); + checkRes( + TimeRangeIteratorFactory.getTimeRangeIterator(0, 32, 4, 6, false, false, false, true, true), + res4_6); + } + + @Test + public void testNaturalMonthTimeRange() { + String[] res1 = { + "[ 1604102400000 : 1606694399999 ]", + "[ 1606694400000 : 1609372799999 ]", + "[ 1609372800000 : 1612051199999 ]", + "[ 1612051200000 : 1614470399999 ]", + "[ 1614470400000 : 1617148799999 ]" + }; + String[] res2 = { + "[ 1604102400000 : 1604966399999 ]", + "[ 1606694400000 : 1607558399999 ]", + "[ 1609372800000 : 1610236799999 ]", + "[ 1612051200000 : 1612915199999 ]", + "[ 1614470400000 : 1615334399999 ]" + }; + String[] res3 = { + "[ 1604102400000 : 1606694399999 ]", + "[ 1604966400000 : 1607558399999 ]", + "[ 1605830400000 : 1608422399999 ]", + "[ 1606694400000 : 1609372799999 ]", + "[ 1607558400000 : 1610236799999 ]", + "[ 1608422400000 : 1611100799999 ]", + "[ 1609286400000 : 1611964799999 ]", + "[ 1610150400000 : 1612828799999 ]", + "[ 1611014400000 : 1613692799999 ]", + "[ 1611878400000 : 1614470399999 ]", + "[ 1612742400000 : 1615161599999 ]", + "[ 1613606400000 : 1616025599999 ]", + "[ 1614470400000 : 1617148799999 ]", + "[ 1615334400000 : 1617148799999 ]", + "[ 1616198400000 : 1617148799999 ]", + "[ 1617062400000 : 1617148799999 ]" + }; + String[] res4 = { + "[ 1604102400000 : 1604966399999 ]", + "[ 1604966400000 : 1605830399999 ]", + "[ 1605830400000 : 1606694399999 ]", + "[ 1606694400000 : 1607558399999 ]", + "[ 1607558400000 : 1608422399999 ]", + "[ 1608422400000 : 1609286399999 ]", + "[ 1609286400000 : 1609372799999 ]", + "[ 1609372800000 : 1610150399999 ]", + "[ 1610150400000 : 1610236799999 ]", + "[ 1610236800000 : 1611014399999 ]", + "[ 1611014400000 : 1611100799999 ]", + "[ 1611100800000 : 1611878399999 ]", + "[ 1611878400000 : 1611964799999 ]", + "[ 1611964800000 : 1612742399999 ]", + "[ 1612742400000 : 1612828799999 ]", + "[ 1612828800000 : 1613606399999 ]", + "[ 1613606400000 : 1613692799999 ]", + "[ 1613692800000 : 1614470399999 ]", + "[ 1614470400000 : 1615161599999 ]", + "[ 1615161600000 : 1615334399999 ]", + "[ 1615334400000 : 1616025599999 ]", + "[ 1616025600000 : 1616198399999 ]", + "[ 1616198400000 : 1617062399999 ]", + "[ 1617062400000 : 1617148799999 ]" + }; + checkRes( + TimeRangeIteratorFactory.getTimeRangeIterator( + 1604102400000L, 1617148800000L, 1, 1, true, true, true, true, false), + res1); + checkRes( + TimeRangeIteratorFactory.getTimeRangeIterator( + 1604102400000L, 1617148800000L, 1, 1, true, true, true, true, true), + res1); + checkRes( + TimeRangeIteratorFactory.getTimeRangeIterator( + 1604102400000L, 1617148800000L, 864000000, 1, true, false, true, true, false), + res2); + checkRes( + TimeRangeIteratorFactory.getTimeRangeIterator( + 1604102400000L, 1617148800000L, 864000000, 1, true, false, true, true, true), + res2); + checkRes( + TimeRangeIteratorFactory.getTimeRangeIterator( + 1604102400000L, 1617148800000L, 1, 864000000, true, true, false, true, false), + res3); + checkRes( + TimeRangeIteratorFactory.getTimeRangeIterator( + 1604102400000L, 1617148800000L, 1, 864000000, true, true, false, true, true), + res4); + } + + private void checkRes(ITimeRangeIterator timeRangeIterator, String[] res) { + boolean isAscending = timeRangeIterator.isAscending(); + int cnt = isAscending ? 0 : res.length - 1; + + // test next time ranges + while (timeRangeIterator.hasNextTimeRange()) { + TimeRange curTimeRange = timeRangeIterator.nextTimeRange(); + Assert.assertEquals(res[cnt], curTimeRange.toString()); + cnt += isAscending ? 1 : -1; + } + } +} diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/AggregateOperatorTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/AggregateOperatorTest.java new file mode 100644 index 000000000000..d4a3392e97ca --- /dev/null +++ b/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/AggregateOperatorTest.java @@ -0,0 +1,321 @@ +/* + * 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.iotdb.db.mpp.execution.operator; + +import org.apache.iotdb.commons.concurrent.IoTDBThreadPoolFactory; +import org.apache.iotdb.commons.exception.IllegalPathException; +import org.apache.iotdb.commons.exception.MetadataException; +import org.apache.iotdb.db.engine.querycontext.QueryDataSource; +import org.apache.iotdb.db.engine.storagegroup.TsFileResource; +import org.apache.iotdb.db.metadata.path.MeasurementPath; +import org.apache.iotdb.db.mpp.aggregation.Accumulator; +import org.apache.iotdb.db.mpp.aggregation.AccumulatorFactory; +import org.apache.iotdb.db.mpp.aggregation.Aggregator; +import org.apache.iotdb.db.mpp.common.FragmentInstanceId; +import org.apache.iotdb.db.mpp.common.PlanFragmentId; +import org.apache.iotdb.db.mpp.common.QueryId; +import org.apache.iotdb.db.mpp.execution.fragment.FragmentInstanceContext; +import org.apache.iotdb.db.mpp.execution.fragment.FragmentInstanceStateMachine; +import org.apache.iotdb.db.mpp.execution.operator.process.AggregateOperator; +import org.apache.iotdb.db.mpp.execution.operator.source.SeriesAggregateScanOperator; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId; +import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.AggregationStep; +import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.GroupByTimeParameter; +import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.InputLocation; +import org.apache.iotdb.db.query.aggregation.AggregationType; +import org.apache.iotdb.db.query.reader.series.SeriesReaderTestUtil; +import org.apache.iotdb.tsfile.exception.write.WriteProcessException; +import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; +import org.apache.iotdb.tsfile.read.common.block.TsBlock; +import org.apache.iotdb.tsfile.write.schema.MeasurementSchema; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ExecutorService; + +import static org.apache.iotdb.db.mpp.execution.fragment.FragmentInstanceContext.createFragmentInstanceContext; +import static org.junit.Assert.assertEquals; + +public class AggregateOperatorTest { + + private static final String AGGREGATE_OPERATOR_TEST_SG = "root.AggregateOperatorTest"; + private final List deviceIds = new ArrayList<>(); + private final List measurementSchemas = new ArrayList<>(); + + private final List seqResources = new ArrayList<>(); + private final List unSeqResources = new ArrayList<>(); + private ExecutorService instanceNotificationExecutor = + IoTDBThreadPoolFactory.newFixedThreadPool(1, "test-instance-notification");; + + @Before + public void setUp() throws MetadataException, IOException, WriteProcessException { + SeriesReaderTestUtil.setUp( + measurementSchemas, deviceIds, seqResources, unSeqResources, AGGREGATE_OPERATOR_TEST_SG); + this.instanceNotificationExecutor = + IoTDBThreadPoolFactory.newFixedThreadPool(1, "test-instance-notification"); + } + + @After + public void tearDown() throws IOException { + SeriesReaderTestUtil.tearDown(seqResources, unSeqResources); + instanceNotificationExecutor.shutdown(); + } + + /** Try to aggregate unary intermediate result of one time series without group by interval. */ + @Test + public void testAggregateIntermediateResult1() throws IllegalPathException { + List aggregationTypes = new ArrayList<>(); + aggregationTypes.add(AggregationType.COUNT); + aggregationTypes.add(AggregationType.SUM); + aggregationTypes.add(AggregationType.MIN_TIME); + aggregationTypes.add(AggregationType.MAX_TIME); + aggregationTypes.add(AggregationType.MAX_VALUE); + aggregationTypes.add(AggregationType.MIN_VALUE); + List> inputLocations = new ArrayList<>(); + for (int i = 0; i < aggregationTypes.size(); i++) { + List inputLocationForOneAggregator = new ArrayList<>(); + inputLocationForOneAggregator.add(new InputLocation[] {new InputLocation(0, i)}); + inputLocationForOneAggregator.add(new InputLocation[] {new InputLocation(1, i)}); + inputLocations.add(inputLocationForOneAggregator); + } + AggregateOperator aggregateOperator = + initAggregateOperator(aggregationTypes, null, inputLocations); + int count = 0; + while (aggregateOperator.hasNext()) { + TsBlock resultTsBlock = aggregateOperator.next(); + assertEquals(500, resultTsBlock.getColumn(0).getLong(0)); + assertEquals(6524750.0, resultTsBlock.getColumn(1).getDouble(0), 0.0001); + assertEquals(0, resultTsBlock.getColumn(2).getLong(0)); + assertEquals(499, resultTsBlock.getColumn(3).getLong(0)); + assertEquals(20199, resultTsBlock.getColumn(4).getInt(0)); + assertEquals(260, resultTsBlock.getColumn(5).getInt(0)); + count++; + } + assertEquals(1, count); + } + + /** Try to aggregate binary intermediate result of one time series without group by interval. */ + @Test + public void testAggregateIntermediateResult2() throws IllegalPathException { + List aggregationTypes = new ArrayList<>(); + aggregationTypes.add(AggregationType.AVG); + aggregationTypes.add(AggregationType.FIRST_VALUE); + aggregationTypes.add(AggregationType.LAST_VALUE); + List> inputLocations = new ArrayList<>(); + for (int i = 0; i < aggregationTypes.size(); i++) { + List inputLocationForOneAggregator = new ArrayList<>(); + inputLocationForOneAggregator.add( + new InputLocation[] {new InputLocation(0, 2 * i), new InputLocation(0, 2 * i + 1)}); + inputLocationForOneAggregator.add( + new InputLocation[] {new InputLocation(1, 2 * i), new InputLocation(1, 2 * i + 1)}); + inputLocations.add(inputLocationForOneAggregator); + } + AggregateOperator aggregateOperator = + initAggregateOperator(aggregationTypes, null, inputLocations); + int count = 0; + while (aggregateOperator.hasNext()) { + TsBlock resultTsBlock = aggregateOperator.next(); + assertEquals(13049.5, resultTsBlock.getColumn(0).getDouble(0), 0.001); + assertEquals(20000, resultTsBlock.getColumn(1).getInt(0)); + assertEquals(10499, resultTsBlock.getColumn(2).getInt(0)); + count++; + } + assertEquals(1, count); + } + + @Test + public void testGroupByIntermediateResult1() throws IllegalPathException { + int[][] result = + new int[][] { + {100, 100, 100, 99}, + {2004950, 2014950, 624950, 834551}, + {0, 100, 200, 300}, + {99, 199, 299, 398}, + {20099, 20199, 10259, 10379}, + {20000, 20100, 260, 380} + }; + GroupByTimeParameter groupByTimeParameter = new GroupByTimeParameter(0, 399, 100, 100, true); + List aggregationTypes = new ArrayList<>(); + aggregationTypes.add(AggregationType.COUNT); + aggregationTypes.add(AggregationType.SUM); + aggregationTypes.add(AggregationType.MIN_TIME); + aggregationTypes.add(AggregationType.MAX_TIME); + aggregationTypes.add(AggregationType.MAX_VALUE); + aggregationTypes.add(AggregationType.MIN_VALUE); + List> inputLocations = new ArrayList<>(); + for (int i = 0; i < aggregationTypes.size(); i++) { + List inputLocationForOneAggregator = new ArrayList<>(); + inputLocationForOneAggregator.add(new InputLocation[] {new InputLocation(0, i)}); + inputLocationForOneAggregator.add(new InputLocation[] {new InputLocation(1, i)}); + inputLocations.add(inputLocationForOneAggregator); + } + AggregateOperator aggregateOperator = + initAggregateOperator(aggregationTypes, groupByTimeParameter, inputLocations); + int count = 0; + while (aggregateOperator.hasNext()) { + TsBlock resultTsBlock = aggregateOperator.next(); + assertEquals(100 * count, resultTsBlock.getTimeColumn().getLong(0)); + assertEquals(result[0][count], resultTsBlock.getColumn(0).getLong(0)); + assertEquals(result[1][count], resultTsBlock.getColumn(1).getDouble(0), 0.0001); + assertEquals(result[2][count], resultTsBlock.getColumn(2).getLong(0)); + assertEquals(result[3][count], resultTsBlock.getColumn(3).getLong(0)); + assertEquals(result[4][count], resultTsBlock.getColumn(4).getInt(0)); + assertEquals(result[5][count], resultTsBlock.getColumn(5).getInt(0)); + count++; + } + assertEquals(4, count); + } + + @Test + public void testGroupByIntermediateResult2() throws IllegalPathException { + double[][] result = + new double[][] { + {20049.5, 20149.5, 6249.5, 8429.808}, + {20000, 20100, 10200, 10300}, + {20099, 20199, 299, 398}, + }; + List aggregationTypes = new ArrayList<>(); + aggregationTypes.add(AggregationType.AVG); + aggregationTypes.add(AggregationType.FIRST_VALUE); + aggregationTypes.add(AggregationType.LAST_VALUE); + GroupByTimeParameter groupByTimeParameter = new GroupByTimeParameter(0, 399, 100, 100, true); + List> inputLocations = new ArrayList<>(); + for (int i = 0; i < aggregationTypes.size(); i++) { + List inputLocationForOneAggregator = new ArrayList<>(); + inputLocationForOneAggregator.add( + new InputLocation[] {new InputLocation(0, 2 * i), new InputLocation(0, 2 * i + 1)}); + inputLocationForOneAggregator.add( + new InputLocation[] {new InputLocation(1, 2 * i), new InputLocation(1, 2 * i + 1)}); + inputLocations.add(inputLocationForOneAggregator); + } + AggregateOperator aggregateOperator = + initAggregateOperator(aggregationTypes, groupByTimeParameter, inputLocations); + int count = 0; + while (aggregateOperator.hasNext()) { + TsBlock resultTsBlock = aggregateOperator.next(); + assertEquals(100 * count, resultTsBlock.getTimeColumn().getLong(0)); + assertEquals(result[0][count], resultTsBlock.getColumn(0).getDouble(0), 0.001); + assertEquals((int) result[1][count], resultTsBlock.getColumn(1).getInt(0)); + assertEquals((int) result[2][count], resultTsBlock.getColumn(2).getInt(0)); + count++; + } + assertEquals(4, count); + } + + /** + * @param aggregationTypes Aggregation function used in test + * @param groupByTimeParameter group by time parameter + * @param inputLocations each inputLocation is used in one aggregator + */ + private AggregateOperator initAggregateOperator( + List aggregationTypes, + GroupByTimeParameter groupByTimeParameter, + List> inputLocations) + throws IllegalPathException { + // Construct operator tree + QueryId queryId = new QueryId("stub_query"); + FragmentInstanceId instanceId = + new FragmentInstanceId(new PlanFragmentId(queryId, 0), "stub-instance"); + FragmentInstanceStateMachine stateMachine = + new FragmentInstanceStateMachine(instanceId, instanceNotificationExecutor); + FragmentInstanceContext fragmentInstanceContext = + createFragmentInstanceContext(instanceId, stateMachine); + PlanNodeId planNodeId1 = new PlanNodeId("1"); + fragmentInstanceContext.addOperatorContext( + 1, planNodeId1, SeriesAggregateScanOperator.class.getSimpleName()); + PlanNodeId planNodeId2 = new PlanNodeId("2"); + fragmentInstanceContext.addOperatorContext( + 2, planNodeId2, SeriesAggregateScanOperator.class.getSimpleName()); + PlanNodeId planNodeId3 = new PlanNodeId("3"); + fragmentInstanceContext.addOperatorContext( + 3, planNodeId3, AggregateOperator.class.getSimpleName()); + + MeasurementPath measurementPath1 = + new MeasurementPath(AGGREGATE_OPERATOR_TEST_SG + ".device0.sensor0", TSDataType.INT32); + List aggregators = new ArrayList<>(); + AccumulatorFactory.createAccumulators(aggregationTypes, TSDataType.INT32, true) + .forEach(o -> aggregators.add(new Aggregator(o, AggregationStep.PARTIAL))); + SeriesAggregateScanOperator seriesAggregateScanOperator1 = + new SeriesAggregateScanOperator( + planNodeId1, + measurementPath1, + Collections.singleton("sensor0"), + fragmentInstanceContext.getOperatorContexts().get(0), + aggregators, + null, + true, + groupByTimeParameter); + List seqResources1 = new ArrayList<>(); + List unSeqResources1 = new ArrayList<>(); + seqResources1.add(seqResources.get(0)); + seqResources1.add(seqResources.get(1)); + seqResources1.add(seqResources.get(3)); + unSeqResources1.add(unSeqResources.get(0)); + unSeqResources1.add(unSeqResources.get(1)); + unSeqResources1.add(unSeqResources.get(3)); + unSeqResources1.add(unSeqResources.get(5)); + seriesAggregateScanOperator1.initQueryDataSource( + new QueryDataSource(seqResources1, unSeqResources1)); + + SeriesAggregateScanOperator seriesAggregateScanOperator2 = + new SeriesAggregateScanOperator( + planNodeId2, + measurementPath1, + Collections.singleton("sensor0"), + fragmentInstanceContext.getOperatorContexts().get(1), + aggregators, + null, + true, + groupByTimeParameter); + List seqResources2 = new ArrayList<>(); + List unSeqResources2 = new ArrayList<>(); + seqResources2.add(seqResources.get(2)); + seqResources2.add(seqResources.get(4)); + unSeqResources2.add(unSeqResources.get(2)); + unSeqResources2.add(unSeqResources.get(4)); + seriesAggregateScanOperator2.initQueryDataSource( + new QueryDataSource(seqResources2, unSeqResources2)); + + List children = new ArrayList<>(); + children.add(seriesAggregateScanOperator1); + children.add(seriesAggregateScanOperator2); + + List finalAggregators = new ArrayList<>(); + List accumulators = + AccumulatorFactory.createAccumulators(aggregationTypes, TSDataType.INT32, true); + for (int i = 0; i < accumulators.size(); i++) { + finalAggregators.add( + new Aggregator(accumulators.get(i), AggregationStep.FINAL, inputLocations.get(i))); + } + + return new AggregateOperator( + fragmentInstanceContext.getOperatorContexts().get(2), + finalAggregators, + children, + true, + groupByTimeParameter); + } +} diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/RawDataAggregateOperatorTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/RawDataAggregateOperatorTest.java new file mode 100644 index 000000000000..0de2bd6b353c --- /dev/null +++ b/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/RawDataAggregateOperatorTest.java @@ -0,0 +1,368 @@ +/* + * 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.iotdb.db.mpp.execution.operator; + +import org.apache.iotdb.commons.concurrent.IoTDBThreadPoolFactory; +import org.apache.iotdb.commons.exception.IllegalPathException; +import org.apache.iotdb.commons.exception.MetadataException; +import org.apache.iotdb.db.engine.querycontext.QueryDataSource; +import org.apache.iotdb.db.engine.storagegroup.TsFileResource; +import org.apache.iotdb.db.metadata.path.MeasurementPath; +import org.apache.iotdb.db.mpp.aggregation.Accumulator; +import org.apache.iotdb.db.mpp.aggregation.AccumulatorFactory; +import org.apache.iotdb.db.mpp.aggregation.Aggregator; +import org.apache.iotdb.db.mpp.common.FragmentInstanceId; +import org.apache.iotdb.db.mpp.common.PlanFragmentId; +import org.apache.iotdb.db.mpp.common.QueryId; +import org.apache.iotdb.db.mpp.execution.fragment.FragmentInstanceContext; +import org.apache.iotdb.db.mpp.execution.fragment.FragmentInstanceStateMachine; +import org.apache.iotdb.db.mpp.execution.operator.process.RawDataAggregateOperator; +import org.apache.iotdb.db.mpp.execution.operator.process.TimeJoinOperator; +import org.apache.iotdb.db.mpp.execution.operator.process.merge.AscTimeComparator; +import org.apache.iotdb.db.mpp.execution.operator.process.merge.SingleColumnMerger; +import org.apache.iotdb.db.mpp.execution.operator.source.SeriesScanOperator; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId; +import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.AggregationStep; +import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.GroupByTimeParameter; +import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.InputLocation; +import org.apache.iotdb.db.mpp.plan.statement.component.OrderBy; +import org.apache.iotdb.db.query.aggregation.AggregationType; +import org.apache.iotdb.db.query.reader.series.SeriesReaderTestUtil; +import org.apache.iotdb.tsfile.exception.write.WriteProcessException; +import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; +import org.apache.iotdb.tsfile.read.common.block.TsBlock; +import org.apache.iotdb.tsfile.write.schema.MeasurementSchema; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ExecutorService; + +import static org.apache.iotdb.db.mpp.execution.fragment.FragmentInstanceContext.createFragmentInstanceContext; +import static org.junit.Assert.assertEquals; + +public class RawDataAggregateOperatorTest { + + private static final String AGGREGATE_OPERATOR_TEST_SG = "root.RawDataAggregateOperatorTest"; + private final List deviceIds = new ArrayList<>(); + private final List measurementSchemas = new ArrayList<>(); + + private final List seqResources = new ArrayList<>(); + private final List unSeqResources = new ArrayList<>(); + private ExecutorService instanceNotificationExecutor; + + @Before + public void setUp() throws MetadataException, IOException, WriteProcessException { + SeriesReaderTestUtil.setUp( + measurementSchemas, deviceIds, seqResources, unSeqResources, AGGREGATE_OPERATOR_TEST_SG); + this.instanceNotificationExecutor = + IoTDBThreadPoolFactory.newFixedThreadPool(1, "test-instance-notification"); + } + + @After + public void tearDown() throws IOException { + SeriesReaderTestUtil.tearDown(seqResources, unSeqResources); + instanceNotificationExecutor.shutdown(); + } + + /** + * Test aggregating raw data without group by interval. + * + *

Example SQL: select count(s0), sum(s0), min_time(s0), max_time(s0), min_value(s0), + * max_value(s0), count(s1), sum(s1), min_time(s1), max_time(s1), min_value(s1), max_value(s1) + * from root.sg.d0 where s1 > 10 and s2 < 15 + * + *

For convenience, we don't use FilterOperator in test though rawDataAggregateOperator is + * always used with value filter. + */ + @Test + public void aggregateRawDataTest1() throws IllegalPathException { + List aggregationTypes = new ArrayList<>(); + List> inputLocations = new ArrayList<>(); + for (int i = 0; i < 2; i++) { + aggregationTypes.add(AggregationType.COUNT); + aggregationTypes.add(AggregationType.SUM); + aggregationTypes.add(AggregationType.MIN_TIME); + aggregationTypes.add(AggregationType.MAX_TIME); + aggregationTypes.add(AggregationType.MAX_VALUE); + aggregationTypes.add(AggregationType.MIN_VALUE); + for (int j = 0; j < 6; j++) { + List inputLocationForOneAggregator = new ArrayList<>(); + inputLocationForOneAggregator.add(new InputLocation[] {new InputLocation(0, i)}); + inputLocations.add(inputLocationForOneAggregator); + } + } + + RawDataAggregateOperator rawDataAggregateOperator = + initRawDataAggregateOperator(aggregationTypes, null, inputLocations); + int count = 0; + while (rawDataAggregateOperator.hasNext()) { + TsBlock resultTsBlock = rawDataAggregateOperator.next(); + for (int i = 0; i < 2; i++) { + assertEquals(500, resultTsBlock.getColumn(6 * i).getLong(0)); + assertEquals(6524750.0, resultTsBlock.getColumn(6 * i + 1).getDouble(0), 0.0001); + assertEquals(0, resultTsBlock.getColumn(6 * i + 2).getLong(0)); + assertEquals(499, resultTsBlock.getColumn(6 * i + 3).getLong(0)); + assertEquals(20199, resultTsBlock.getColumn(6 * i + 4).getInt(0)); + assertEquals(260, resultTsBlock.getColumn(6 * i + 5).getInt(0)); + } + count++; + } + assertEquals(1, count); + } + + /** + * Test aggregating raw data without group by interval. + * + *

Example SQL: select avg(s0), avg(s1), first_value(s0), first_value(s1), last_value(s0), + * last_value(s1) from root.sg.d0 where s1 > 10 and s2 < 15 + * + *

For convenience, we don't use FilterOperator in test though rawDataAggregateOperator is + * always used with value filter. + */ + @Test + public void aggregateRawDataTest2() throws IllegalPathException { + List aggregationTypes = new ArrayList<>(); + List> inputLocations = new ArrayList<>(); + for (int i = 0; i < 2; i++) { + aggregationTypes.add(AggregationType.AVG); + List inputLocationForOneAggregator = new ArrayList<>(); + inputLocationForOneAggregator.add(new InputLocation[] {new InputLocation(0, i)}); + inputLocations.add(inputLocationForOneAggregator); + } + for (int i = 0; i < 2; i++) { + aggregationTypes.add(AggregationType.FIRST_VALUE); + List inputLocationForOneAggregator = new ArrayList<>(); + inputLocationForOneAggregator.add(new InputLocation[] {new InputLocation(0, i)}); + inputLocations.add(inputLocationForOneAggregator); + } + for (int i = 0; i < 2; i++) { + aggregationTypes.add(AggregationType.LAST_VALUE); + List inputLocationForOneAggregator = new ArrayList<>(); + inputLocationForOneAggregator.add(new InputLocation[] {new InputLocation(0, i)}); + inputLocations.add(inputLocationForOneAggregator); + } + + RawDataAggregateOperator rawDataAggregateOperator = + initRawDataAggregateOperator(aggregationTypes, null, inputLocations); + int count = 0; + while (rawDataAggregateOperator.hasNext()) { + TsBlock resultTsBlock = rawDataAggregateOperator.next(); + for (int i = 0; i < 2; i++) { + assertEquals(13049.5, resultTsBlock.getColumn(i).getDouble(0), 0.001); + } + for (int i = 2; i < 4; i++) { + assertEquals(20000, resultTsBlock.getColumn(i).getInt(0)); + } + for (int i = 4; i < 6; i++) { + assertEquals(10499, resultTsBlock.getColumn(i).getInt(0)); + } + count++; + } + assertEquals(1, count); + } + + /** Test aggregating raw data by time interval. */ + @Test + public void groupByRawDataTest1() throws IllegalPathException { + int[][] result = + new int[][] { + {100, 100, 100, 99}, + {2004950, 2014950, 624950, 834551}, + {0, 100, 200, 300}, + {99, 199, 299, 398}, + {20099, 20199, 10259, 10379}, + {20000, 20100, 260, 380} + }; + List aggregationTypes = new ArrayList<>(); + List> inputLocations = new ArrayList<>(); + for (int i = 0; i < 2; i++) { + aggregationTypes.add(AggregationType.COUNT); + aggregationTypes.add(AggregationType.SUM); + aggregationTypes.add(AggregationType.MIN_TIME); + aggregationTypes.add(AggregationType.MAX_TIME); + aggregationTypes.add(AggregationType.MAX_VALUE); + aggregationTypes.add(AggregationType.MIN_VALUE); + for (int j = 0; j < 6; j++) { + List inputLocationForOneAggregator = new ArrayList<>(); + inputLocationForOneAggregator.add(new InputLocation[] {new InputLocation(0, i)}); + inputLocations.add(inputLocationForOneAggregator); + } + } + GroupByTimeParameter groupByTimeParameter = new GroupByTimeParameter(0, 399, 100, 100, true); + + RawDataAggregateOperator rawDataAggregateOperator = + initRawDataAggregateOperator(aggregationTypes, groupByTimeParameter, inputLocations); + int count = 0; + while (rawDataAggregateOperator.hasNext()) { + TsBlock resultTsBlock = rawDataAggregateOperator.next(); + assertEquals(100 * count, resultTsBlock.getTimeColumn().getLong(0)); + for (int i = 0; i < 2; i++) { + assertEquals(result[0][count], resultTsBlock.getColumn(6 * i).getLong(0)); + assertEquals(result[1][count], resultTsBlock.getColumn(6 * i + 1).getDouble(0), 0.0001); + assertEquals(result[2][count], resultTsBlock.getColumn(6 * i + 2).getLong(0)); + assertEquals(result[3][count], resultTsBlock.getColumn(6 * i + 3).getLong(0)); + assertEquals(result[4][count], resultTsBlock.getColumn(6 * i + 4).getInt(0)); + assertEquals(result[5][count], resultTsBlock.getColumn(6 * i + 5).getInt(0)); + } + count++; + } + assertEquals(4, count); + } + + /** Test aggregating raw data by time interval. */ + @Test + public void groupByRawDataTest2() throws IllegalPathException { + double[][] result = + new double[][] { + {20049.5, 20149.5, 6249.5, 8429.808}, + {20000, 20100, 10200, 10300}, + {20099, 20199, 299, 398}, + }; + List aggregationTypes = new ArrayList<>(); + List> inputLocations = new ArrayList<>(); + for (int i = 0; i < 2; i++) { + aggregationTypes.add(AggregationType.AVG); + List inputLocationForOneAggregator = new ArrayList<>(); + inputLocationForOneAggregator.add(new InputLocation[] {new InputLocation(0, i)}); + inputLocations.add(inputLocationForOneAggregator); + } + for (int i = 0; i < 2; i++) { + aggregationTypes.add(AggregationType.FIRST_VALUE); + List inputLocationForOneAggregator = new ArrayList<>(); + inputLocationForOneAggregator.add(new InputLocation[] {new InputLocation(0, i)}); + inputLocations.add(inputLocationForOneAggregator); + } + for (int i = 0; i < 2; i++) { + aggregationTypes.add(AggregationType.LAST_VALUE); + List inputLocationForOneAggregator = new ArrayList<>(); + inputLocationForOneAggregator.add(new InputLocation[] {new InputLocation(0, i)}); + inputLocations.add(inputLocationForOneAggregator); + } + GroupByTimeParameter groupByTimeParameter = new GroupByTimeParameter(0, 399, 100, 100, true); + RawDataAggregateOperator rawDataAggregateOperator = + initRawDataAggregateOperator(aggregationTypes, groupByTimeParameter, inputLocations); + int count = 0; + while (rawDataAggregateOperator.hasNext()) { + TsBlock resultTsBlock = rawDataAggregateOperator.next(); + assertEquals(100 * count, resultTsBlock.getTimeColumn().getLong(0)); + for (int i = 0; i < 2; i++) { + assertEquals(result[0][count], resultTsBlock.getColumn(i).getDouble(0), 0.001); + } + for (int i = 2; i < 4; i++) { + assertEquals((int) result[1][count], resultTsBlock.getColumn(i).getInt(0)); + } + for (int i = 4; i < 6; i++) { + assertEquals((int) result[2][count], resultTsBlock.getColumn(i).getInt(0)); + } + count++; + } + assertEquals(4, count); + } + + private RawDataAggregateOperator initRawDataAggregateOperator( + List aggregationTypes, + GroupByTimeParameter groupByTimeParameter, + List> inputLocations) + throws IllegalPathException { + ExecutorService instanceNotificationExecutor = + IoTDBThreadPoolFactory.newFixedThreadPool(1, "test-instance-notification"); + + MeasurementPath measurementPath1 = + new MeasurementPath(AGGREGATE_OPERATOR_TEST_SG + ".device0.sensor0", TSDataType.INT32); + Set allSensors = new HashSet<>(); + allSensors.add("sensor0"); + allSensors.add("sensor1"); + QueryId queryId = new QueryId("stub_query"); + FragmentInstanceId instanceId = + new FragmentInstanceId(new PlanFragmentId(queryId, 0), "stub-instance"); + FragmentInstanceStateMachine stateMachine = + new FragmentInstanceStateMachine(instanceId, instanceNotificationExecutor); + FragmentInstanceContext fragmentInstanceContext = + createFragmentInstanceContext(instanceId, stateMachine); + PlanNodeId planNodeId1 = new PlanNodeId("1"); + fragmentInstanceContext.addOperatorContext( + 1, planNodeId1, SeriesScanOperator.class.getSimpleName()); + PlanNodeId planNodeId2 = new PlanNodeId("2"); + fragmentInstanceContext.addOperatorContext( + 2, planNodeId2, SeriesScanOperator.class.getSimpleName()); + fragmentInstanceContext.addOperatorContext( + 3, new PlanNodeId("3"), TimeJoinOperator.class.getSimpleName()); + fragmentInstanceContext.addOperatorContext( + 4, new PlanNodeId("4"), RawDataAggregateOperatorTest.class.getSimpleName()); + SeriesScanOperator seriesScanOperator1 = + new SeriesScanOperator( + planNodeId1, + measurementPath1, + allSensors, + TSDataType.INT32, + fragmentInstanceContext.getOperatorContexts().get(0), + null, + null, + true); + seriesScanOperator1.initQueryDataSource(new QueryDataSource(seqResources, unSeqResources)); + + MeasurementPath measurementPath2 = + new MeasurementPath(AGGREGATE_OPERATOR_TEST_SG + ".device0.sensor1", TSDataType.INT32); + SeriesScanOperator seriesScanOperator2 = + new SeriesScanOperator( + planNodeId2, + measurementPath2, + allSensors, + TSDataType.INT32, + fragmentInstanceContext.getOperatorContexts().get(1), + null, + null, + true); + seriesScanOperator2.initQueryDataSource(new QueryDataSource(seqResources, unSeqResources)); + + TimeJoinOperator timeJoinOperator = + new TimeJoinOperator( + fragmentInstanceContext.getOperatorContexts().get(2), + Arrays.asList(seriesScanOperator1, seriesScanOperator2), + OrderBy.TIMESTAMP_ASC, + Arrays.asList(TSDataType.INT32, TSDataType.INT32), + Arrays.asList( + new SingleColumnMerger(new InputLocation(0, 0), new AscTimeComparator()), + new SingleColumnMerger(new InputLocation(1, 0), new AscTimeComparator())), + new AscTimeComparator()); + + List aggregators = new ArrayList<>(); + List accumulators = + AccumulatorFactory.createAccumulators(aggregationTypes, TSDataType.INT32, true); + for (int i = 0; i < accumulators.size(); i++) { + aggregators.add( + new Aggregator(accumulators.get(i), AggregationStep.SINGLE, inputLocations.get(i))); + } + return new RawDataAggregateOperator( + fragmentInstanceContext.getOperatorContexts().get(3), + aggregators, + timeJoinOperator, + true, + groupByTimeParameter); + } +} diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/SeriesAggregateScanOperatorTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/SeriesAggregateScanOperatorTest.java index a109b52609f3..1c7a77ded6e4 100644 --- a/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/SeriesAggregateScanOperatorTest.java +++ b/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/SeriesAggregateScanOperatorTest.java @@ -104,6 +104,23 @@ public void testAggregationWithoutTimeFilter() throws IllegalPathException { assertEquals(1, count); } + @Test + public void testAggregationWithoutTimeFilterOrderByTimeDesc() throws IllegalPathException { + List aggregationTypes = Collections.singletonList(AggregationType.COUNT); + List aggregators = new ArrayList<>(); + AccumulatorFactory.createAccumulators(aggregationTypes, TSDataType.INT32, true) + .forEach(o -> aggregators.add(new Aggregator(o, AggregationStep.SINGLE))); + SeriesAggregateScanOperator seriesAggregateScanOperator = + initSeriesAggregateScanOperator(aggregators, null, false, null); + int count = 0; + while (seriesAggregateScanOperator.hasNext()) { + TsBlock resultTsBlock = seriesAggregateScanOperator.next(); + assertEquals(500, resultTsBlock.getColumn(0).getLong(0)); + count++; + } + assertEquals(1, count); + } + @Test public void testMultiAggregationFuncWithoutTimeFilter1() throws IllegalPathException { List aggregationTypes = new ArrayList<>(); @@ -266,7 +283,7 @@ public void testMultiAggregationWithTimeFilter() throws IllegalPathException { @Test public void testGroupByWithoutGlobalTimeFilter() throws IllegalPathException { - int[] result = new int[] {100, 100, 100, 100}; + int[] result = new int[] {100, 100, 100, 99}; GroupByTimeParameter groupByTimeParameter = new GroupByTimeParameter(0, 399, 100, 100, true); List aggregationTypes = Collections.singletonList(AggregationType.COUNT); List aggregators = new ArrayList<>(); @@ -310,7 +327,7 @@ public void testGroupByWithMultiFunction() throws IllegalPathException { int[][] result = new int[][] { {20000, 20100, 10200, 10300}, - {20099, 20199, 299, 399}, + {20099, 20199, 299, 398}, {20099, 20199, 10259, 10379}, {20000, 20100, 260, 380} }; @@ -343,7 +360,7 @@ public void testGroupByWithMultiFunctionOrderByTimeDesc() throws IllegalPathExce int[][] result = new int[][] { {20000, 20100, 10200, 10300}, - {20099, 20199, 299, 399}, + {20099, 20199, 299, 398}, {20099, 20199, 10259, 10379}, {20000, 20100, 260, 380} }; @@ -373,7 +390,7 @@ public void testGroupByWithMultiFunctionOrderByTimeDesc() throws IllegalPathExce @Test public void testGroupBySlidingTimeWindow() throws IllegalPathException { - int[] result = new int[] {50, 50, 50, 50, 50, 50, 50, 50}; + int[] result = new int[] {50, 50, 50, 50, 50, 50, 50, 49}; GroupByTimeParameter groupByTimeParameter = new GroupByTimeParameter(0, 399, 100, 50, true); List aggregationTypes = Collections.singletonList(AggregationType.COUNT); List aggregators = new ArrayList<>(); diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/process/AggregationNodeSerdeTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/process/AggregationNodeSerdeTest.java index e1ead567593b..1ebddfd73661 100644 --- a/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/process/AggregationNodeSerdeTest.java +++ b/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/process/AggregationNodeSerdeTest.java @@ -43,6 +43,7 @@ import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collections; +import java.util.List; import static org.apache.iotdb.tsfile.read.filter.factory.FilterType.VALUE_FILTER; import static org.junit.Assert.assertEquals; @@ -85,4 +86,231 @@ public void testSerializeAndDeserialize() throws IllegalPathException { byteBuffer.flip(); assertEquals(PlanNodeDeserializeHelper.deserialize(byteBuffer), aggregationNode); } + + /** Test AVG with COUNT and SUM (former or later). */ + @Test + public void getDeduplicatedDescriptorsTest1() { + PartialPath seriesPath1 = new PartialPath(new String[] {"root", "sg", "d1", "s1"}); + List aggregationTypeList = new ArrayList<>(); + aggregationTypeList.add(AggregationType.COUNT); + aggregationTypeList.add(AggregationType.AVG); + aggregationTypeList.add(AggregationType.SUM); + List descriptorList = new ArrayList<>(); + for (AggregationType aggregationType : aggregationTypeList) { + descriptorList.add( + new AggregationDescriptor( + aggregationType, + AggregationStep.PARTIAL, + Collections.singletonList(new TimeSeriesOperand(seriesPath1)))); + } + List deduplicatedDescriptors = + AggregationNode.getDeduplicatedDescriptors(descriptorList); + assertEquals(1, deduplicatedDescriptors.size()); + assertEquals(AggregationType.AVG, deduplicatedDescriptors.get(0).getAggregationType()); + + aggregationTypeList = new ArrayList<>(); + aggregationTypeList.add(AggregationType.COUNT); + aggregationTypeList.add(AggregationType.SUM); + aggregationTypeList.add(AggregationType.AVG); + descriptorList = new ArrayList<>(); + for (AggregationType aggregationType : aggregationTypeList) { + descriptorList.add( + new AggregationDescriptor( + aggregationType, + AggregationStep.PARTIAL, + Collections.singletonList(new TimeSeriesOperand(seriesPath1)))); + } + deduplicatedDescriptors = AggregationNode.getDeduplicatedDescriptors(descriptorList); + assertEquals(1, deduplicatedDescriptors.size()); + assertEquals(AggregationType.AVG, deduplicatedDescriptors.get(0).getAggregationType()); + + aggregationTypeList = new ArrayList<>(); + aggregationTypeList.add(AggregationType.AVG); + aggregationTypeList.add(AggregationType.COUNT); + aggregationTypeList.add(AggregationType.SUM); + descriptorList = new ArrayList<>(); + for (AggregationType aggregationType : aggregationTypeList) { + descriptorList.add( + new AggregationDescriptor( + aggregationType, + AggregationStep.PARTIAL, + Collections.singletonList(new TimeSeriesOperand(seriesPath1)))); + } + deduplicatedDescriptors = AggregationNode.getDeduplicatedDescriptors(descriptorList); + assertEquals(1, deduplicatedDescriptors.size()); + assertEquals(AggregationType.AVG, deduplicatedDescriptors.get(0).getAggregationType()); + + // try output final + descriptorList = new ArrayList<>(); + for (AggregationType aggregationType : aggregationTypeList) { + descriptorList.add( + new AggregationDescriptor( + aggregationType, + AggregationStep.SINGLE, + Collections.singletonList(new TimeSeriesOperand(seriesPath1)))); + } + deduplicatedDescriptors = AggregationNode.getDeduplicatedDescriptors(descriptorList); + assertEquals(3, deduplicatedDescriptors.size()); + assertEquals(AggregationType.AVG, deduplicatedDescriptors.get(0).getAggregationType()); + assertEquals(AggregationType.COUNT, deduplicatedDescriptors.get(1).getAggregationType()); + assertEquals(AggregationType.SUM, deduplicatedDescriptors.get(2).getAggregationType()); + } + + /** Test FIRST_VALUE with MIN_TIME (former or later). */ + @Test + public void getDeduplicatedDescriptorsTest2() { + PartialPath seriesPath1 = new PartialPath(new String[] {"root", "sg", "d1", "s1"}); + List aggregationTypeList = new ArrayList<>(); + aggregationTypeList.add(AggregationType.FIRST_VALUE); + aggregationTypeList.add(AggregationType.MIN_TIME); + List descriptorList = new ArrayList<>(); + for (AggregationType aggregationType : aggregationTypeList) { + descriptorList.add( + new AggregationDescriptor( + aggregationType, + AggregationStep.PARTIAL, + Collections.singletonList(new TimeSeriesOperand(seriesPath1)))); + } + List deduplicatedDescriptors = + AggregationNode.getDeduplicatedDescriptors(descriptorList); + assertEquals(1, deduplicatedDescriptors.size()); + assertEquals(AggregationType.FIRST_VALUE, deduplicatedDescriptors.get(0).getAggregationType()); + + aggregationTypeList = new ArrayList<>(); + aggregationTypeList.add(AggregationType.MIN_TIME); + aggregationTypeList.add(AggregationType.FIRST_VALUE); + descriptorList = new ArrayList<>(); + for (AggregationType aggregationType : aggregationTypeList) { + descriptorList.add( + new AggregationDescriptor( + aggregationType, + AggregationStep.PARTIAL, + Collections.singletonList(new TimeSeriesOperand(seriesPath1)))); + } + deduplicatedDescriptors = AggregationNode.getDeduplicatedDescriptors(descriptorList); + assertEquals(1, deduplicatedDescriptors.size()); + assertEquals(AggregationType.FIRST_VALUE, deduplicatedDescriptors.get(0).getAggregationType()); + + // try output final + descriptorList = new ArrayList<>(); + for (AggregationType aggregationType : aggregationTypeList) { + descriptorList.add( + new AggregationDescriptor( + aggregationType, + AggregationStep.SINGLE, + Collections.singletonList(new TimeSeriesOperand(seriesPath1)))); + } + deduplicatedDescriptors = AggregationNode.getDeduplicatedDescriptors(descriptorList); + assertEquals(2, deduplicatedDescriptors.size()); + assertEquals(AggregationType.MIN_TIME, deduplicatedDescriptors.get(0).getAggregationType()); + assertEquals(AggregationType.FIRST_VALUE, deduplicatedDescriptors.get(1).getAggregationType()); + } + + /** Test LAST_VALUE with MAX_TIME (former or later). */ + @Test + public void getDeduplicatedDescriptorsTest3() { + PartialPath seriesPath1 = new PartialPath(new String[] {"root", "sg", "d1", "s1"}); + List aggregationTypeList = new ArrayList<>(); + aggregationTypeList.add(AggregationType.LAST_VALUE); + aggregationTypeList.add(AggregationType.MAX_TIME); + List descriptorList = new ArrayList<>(); + for (AggregationType aggregationType : aggregationTypeList) { + descriptorList.add( + new AggregationDescriptor( + aggregationType, + AggregationStep.PARTIAL, + Collections.singletonList(new TimeSeriesOperand(seriesPath1)))); + } + List deduplicatedDescriptors = + AggregationNode.getDeduplicatedDescriptors(descriptorList); + assertEquals(1, deduplicatedDescriptors.size()); + assertEquals(AggregationType.LAST_VALUE, deduplicatedDescriptors.get(0).getAggregationType()); + + aggregationTypeList = new ArrayList<>(); + aggregationTypeList.add(AggregationType.MAX_TIME); + aggregationTypeList.add(AggregationType.LAST_VALUE); + descriptorList = new ArrayList<>(); + for (AggregationType aggregationType : aggregationTypeList) { + descriptorList.add( + new AggregationDescriptor( + aggregationType, + AggregationStep.PARTIAL, + Collections.singletonList(new TimeSeriesOperand(seriesPath1)))); + } + deduplicatedDescriptors = AggregationNode.getDeduplicatedDescriptors(descriptorList); + assertEquals(1, deduplicatedDescriptors.size()); + assertEquals(AggregationType.LAST_VALUE, deduplicatedDescriptors.get(0).getAggregationType()); + + // try output final + descriptorList = new ArrayList<>(); + for (AggregationType aggregationType : aggregationTypeList) { + descriptorList.add( + new AggregationDescriptor( + aggregationType, + AggregationStep.SINGLE, + Collections.singletonList(new TimeSeriesOperand(seriesPath1)))); + } + deduplicatedDescriptors = AggregationNode.getDeduplicatedDescriptors(descriptorList); + assertEquals(2, deduplicatedDescriptors.size()); + assertEquals(AggregationType.MAX_TIME, deduplicatedDescriptors.get(0).getAggregationType()); + assertEquals(AggregationType.LAST_VALUE, deduplicatedDescriptors.get(1).getAggregationType()); + } + + /** Test AVG with COUNT but work on different time series. */ + @Test + public void getDeduplicatedDescriptorsTest4() { + PartialPath seriesPath1 = new PartialPath(new String[] {"root", "sg", "d1", "s1"}); + PartialPath seriesPath2 = new PartialPath(new String[] {"root", "sg", "d1", "s2"}); + List descriptorList = new ArrayList<>(); + descriptorList.add( + new AggregationDescriptor( + AggregationType.AVG, + AggregationStep.PARTIAL, + Collections.singletonList(new TimeSeriesOperand(seriesPath1)))); + descriptorList.add( + new AggregationDescriptor( + AggregationType.COUNT, + AggregationStep.PARTIAL, + Collections.singletonList(new TimeSeriesOperand(seriesPath2)))); + + List deduplicatedDescriptors = + AggregationNode.getDeduplicatedDescriptors(descriptorList); + assertEquals(2, deduplicatedDescriptors.size()); + assertEquals(AggregationType.AVG, deduplicatedDescriptors.get(0).getAggregationType()); + assertEquals(AggregationType.COUNT, deduplicatedDescriptors.get(1).getAggregationType()); + + descriptorList = new ArrayList<>(); + descriptorList.add( + new AggregationDescriptor( + AggregationType.FIRST_VALUE, + AggregationStep.PARTIAL, + Collections.singletonList(new TimeSeriesOperand(seriesPath1)))); + descriptorList.add( + new AggregationDescriptor( + AggregationType.MIN_TIME, + AggregationStep.PARTIAL, + Collections.singletonList(new TimeSeriesOperand(seriesPath2)))); + + deduplicatedDescriptors = AggregationNode.getDeduplicatedDescriptors(descriptorList); + assertEquals(2, deduplicatedDescriptors.size()); + assertEquals(AggregationType.FIRST_VALUE, deduplicatedDescriptors.get(0).getAggregationType()); + assertEquals(AggregationType.MIN_TIME, deduplicatedDescriptors.get(1).getAggregationType()); + + descriptorList = new ArrayList<>(); + descriptorList.add( + new AggregationDescriptor( + AggregationType.LAST_VALUE, + AggregationStep.PARTIAL, + Collections.singletonList(new TimeSeriesOperand(seriesPath1)))); + descriptorList.add( + new AggregationDescriptor( + AggregationType.MAX_TIME, + AggregationStep.PARTIAL, + Collections.singletonList(new TimeSeriesOperand(seriesPath2)))); + + deduplicatedDescriptors = AggregationNode.getDeduplicatedDescriptors(descriptorList); + assertEquals(2, deduplicatedDescriptors.size()); + assertEquals(AggregationType.LAST_VALUE, deduplicatedDescriptors.get(0).getAggregationType()); + assertEquals(AggregationType.MAX_TIME, deduplicatedDescriptors.get(1).getAggregationType()); + } } diff --git a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/TimeRange.java b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/TimeRange.java index d4cc33b7fcfe..969a13d6372f 100644 --- a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/TimeRange.java +++ b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/TimeRange.java @@ -98,11 +98,27 @@ public boolean contains(TimeRange r) { } public boolean contains(long min, long max) { - return this.min <= min && this.max >= max; + if (leftClose && rightClose) { + return this.min <= min && this.max >= max; + } else if (leftClose) { + return this.min <= min && this.max > max; + } else if (rightClose) { + return this.min < min && this.max >= max; + } else { + return this.min < min && this.max > max; + } } public boolean contains(long time) { - return this.min <= time && time <= this.max; + if (leftClose && rightClose) { + return time >= this.min && time <= this.max; + } else if (leftClose) { + return time >= this.min && time < this.max; + } else if (rightClose) { + return time > this.min && time <= this.max; + } else { + return time > this.min && time < this.max; + } } /** From f7f857e56270bf4ab8177379ac74173efa3417d6 Mon Sep 17 00:00:00 2001 From: ZhangHongYin <46039728+SpriCoder@users.noreply.github.com> Date: Tue, 17 May 2022 11:55:31 +0800 Subject: [PATCH 027/436] [IOTDB-3062] add last cache in DataNodeSchemaCache (#5906) --- .../cache/DataNodeLastCacheManager.java | 81 +++++++++++++++++++ .../metadata/cache/DataNodeSchemaCache.java | 34 ++++++++ .../db/metadata/cache/SchemaCacheEntry.java | 19 +++++ .../cache/DataNodeSchemaCacheTest.java | 69 ++++++++++++++++ 4 files changed, 203 insertions(+) create mode 100644 server/src/main/java/org/apache/iotdb/db/metadata/cache/DataNodeLastCacheManager.java diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/cache/DataNodeLastCacheManager.java b/server/src/main/java/org/apache/iotdb/db/metadata/cache/DataNodeLastCacheManager.java new file mode 100644 index 000000000000..e886f146225d --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/metadata/cache/DataNodeLastCacheManager.java @@ -0,0 +1,81 @@ +/* + * 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.iotdb.db.metadata.cache; + +import org.apache.iotdb.db.conf.IoTDBDescriptor; +import org.apache.iotdb.db.metadata.lastCache.container.ILastCacheContainer; +import org.apache.iotdb.tsfile.read.TimeValuePair; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DataNodeLastCacheManager { + private static final Logger logger = LoggerFactory.getLogger(DataNodeLastCacheManager.class); + + private static final boolean CACHE_ENABLED = + IoTDBDescriptor.getInstance().getConfig().isLastCacheEnabled(); + + /** + * get the last cache value from time series + * + * @param entry schema cache entry in DataNodeSchemaCache + * @return the last cache value + */ + public static TimeValuePair getLastCache(SchemaCacheEntry entry) { + if (null == entry) { + return null; + } + ILastCacheContainer lastCacheContainer = entry.getLastCacheContainer(); + return lastCacheContainer.getCachedLast(); + } + + /** + * update the last cache value of time series + * + * @param entry schema cache entry in DataNodeSchemaCache + * @param timeValuePair the latest point value + * @param highPriorityUpdate the last value from insertPlan is high priority + * @param latestFlushedTime latest flushed time + */ + public static void updateLastCache( + SchemaCacheEntry entry, + TimeValuePair timeValuePair, + boolean highPriorityUpdate, + Long latestFlushedTime) { + if (null == entry) { + return; + } + ILastCacheContainer lastCacheContainer = entry.getLastCacheContainer(); + lastCacheContainer.updateCachedLast(timeValuePair, highPriorityUpdate, latestFlushedTime); + } + + /** + * reset the last cache value of time series + * + * @param entry schema cache entry in DataNodeSchemaCache + */ + public static void resetLastCache(SchemaCacheEntry entry) { + if (null == entry) { + return; + } + ILastCacheContainer lastCacheContainer = entry.getLastCacheContainer(); + lastCacheContainer.resetLastCache(); + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/cache/DataNodeSchemaCache.java b/server/src/main/java/org/apache/iotdb/db/metadata/cache/DataNodeSchemaCache.java index 116d2af8fdc1..f2f6b98c3c72 100644 --- a/server/src/main/java/org/apache/iotdb/db/metadata/cache/DataNodeSchemaCache.java +++ b/server/src/main/java/org/apache/iotdb/db/metadata/cache/DataNodeSchemaCache.java @@ -24,6 +24,7 @@ import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.metadata.path.MeasurementPath; import org.apache.iotdb.db.mpp.common.schematree.SchemaTree; +import org.apache.iotdb.tsfile.read.TimeValuePair; import org.apache.iotdb.tsfile.write.schema.MeasurementSchema; import com.github.benmanes.caffeine.cache.Cache; @@ -96,6 +97,38 @@ public void put(SchemaTree schemaTree) { } } + public TimeValuePair getLastCache(PartialPath seriesPath) { + SchemaCacheEntry entry = cache.getIfPresent(seriesPath); + if (null == entry) { + return null; + } + + return DataNodeLastCacheManager.getLastCache(entry); + } + + public void updateLastCache( + PartialPath seriesPath, + TimeValuePair timeValuePair, + boolean highPriorityUpdate, + Long latestFlushedTime) { + SchemaCacheEntry entry = cache.getIfPresent(seriesPath); + if (null == entry) { + return; + } + + DataNodeLastCacheManager.updateLastCache( + entry, timeValuePair, highPriorityUpdate, latestFlushedTime); + } + + public void resetLastCache(PartialPath seriesPath) { + SchemaCacheEntry entry = cache.getIfPresent(seriesPath); + if (null == entry) { + return; + } + + DataNodeLastCacheManager.resetLastCache(entry); + } + /** * For delete timeseries meatadata cache operation * @@ -103,6 +136,7 @@ public void put(SchemaTree schemaTree) { * @return */ public void invalidate(PartialPath partialPath) { + resetLastCache(partialPath); cache.invalidate(partialPath); } diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/cache/SchemaCacheEntry.java b/server/src/main/java/org/apache/iotdb/db/metadata/cache/SchemaCacheEntry.java index feb8f24eec21..b3638ff58681 100644 --- a/server/src/main/java/org/apache/iotdb/db/metadata/cache/SchemaCacheEntry.java +++ b/server/src/main/java/org/apache/iotdb/db/metadata/cache/SchemaCacheEntry.java @@ -19,6 +19,8 @@ package org.apache.iotdb.db.metadata.cache; +import org.apache.iotdb.db.metadata.lastCache.container.ILastCacheContainer; +import org.apache.iotdb.db.metadata.lastCache.container.LastCacheContainer; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.write.schema.MeasurementSchema; @@ -30,6 +32,8 @@ public class SchemaCacheEntry { private final boolean isAligned; + private volatile ILastCacheContainer lastCacheContainer = null; + SchemaCacheEntry(MeasurementSchema measurementSchema, String alias, boolean isAligned) { this.measurementSchema = measurementSchema; this.alias = alias; @@ -55,4 +59,19 @@ public String getAlias() { public boolean isAligned() { return isAligned; } + + public ILastCacheContainer getLastCacheContainer() { + if (lastCacheContainer == null) { + synchronized (this) { + if (lastCacheContainer == null) { + lastCacheContainer = new LastCacheContainer(); + } + } + } + return lastCacheContainer; + } + + public void setLastCacheContainer(ILastCacheContainer lastCacheContainer) { + this.lastCacheContainer = lastCacheContainer; + } } diff --git a/server/src/test/java/org/apache/iotdb/db/metadata/cache/DataNodeSchemaCacheTest.java b/server/src/test/java/org/apache/iotdb/db/metadata/cache/DataNodeSchemaCacheTest.java index 91f9fbf37526..5e6160c4e835 100644 --- a/server/src/test/java/org/apache/iotdb/db/metadata/cache/DataNodeSchemaCacheTest.java +++ b/server/src/test/java/org/apache/iotdb/db/metadata/cache/DataNodeSchemaCacheTest.java @@ -23,6 +23,8 @@ import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.mpp.common.schematree.SchemaTree; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; +import org.apache.iotdb.tsfile.read.TimeValuePair; +import org.apache.iotdb.tsfile.utils.TsPrimitiveType; import org.apache.iotdb.tsfile.write.schema.MeasurementSchema; import org.junit.After; @@ -106,6 +108,73 @@ public void testGetSchemaEntity() throws IllegalPathException { Assert.assertEquals(5, dataNodeSchemaCache.estimatedSize()); } + @Test + public void testLastCache() throws IllegalPathException { + // test no cache + PartialPath seriesPath1 = new PartialPath("root.sg1.d1.s1"); + PartialPath seriesPath2 = new PartialPath("root.sg1.d1.s2"); + PartialPath seriesPath3 = new PartialPath("root.sg1.d1.s3"); + Assert.assertNull(dataNodeSchemaCache.getLastCache(seriesPath1)); + Assert.assertNull(dataNodeSchemaCache.getLastCache(seriesPath2)); + Assert.assertNull(dataNodeSchemaCache.getLastCache(seriesPath3)); + // test no last cache + dataNodeSchemaCache.put(generateSchemaTree1()); + Assert.assertNull(dataNodeSchemaCache.getLastCache(seriesPath1)); + Assert.assertNull(dataNodeSchemaCache.getLastCache(seriesPath2)); + Assert.assertNull(dataNodeSchemaCache.getLastCache(seriesPath3)); + // put cache + long timestamp = 100; + long timestamp2 = 101; + TsPrimitiveType value = TsPrimitiveType.getByType(TSDataType.INT32, 101); + TsPrimitiveType value2 = TsPrimitiveType.getByType(TSDataType.INT32, 100); + TsPrimitiveType value3 = TsPrimitiveType.getByType(TSDataType.INT32, 99); + + // put into last cache when cache not exist + TimeValuePair timeValuePair = new TimeValuePair(timestamp, value); + dataNodeSchemaCache.updateLastCache(seriesPath1, timeValuePair, false, 99L); + TimeValuePair cachedTimeValuePair = dataNodeSchemaCache.getLastCache(seriesPath1); + Assert.assertNotNull(cachedTimeValuePair); + Assert.assertEquals(timestamp, cachedTimeValuePair.getTimestamp()); + Assert.assertEquals(value, cachedTimeValuePair.getValue()); + Assert.assertNull(dataNodeSchemaCache.getLastCache(seriesPath2)); + Assert.assertNull(dataNodeSchemaCache.getLastCache(seriesPath3)); + + // same time but low priority + TimeValuePair timeValuePair2 = new TimeValuePair(timestamp, value2); + dataNodeSchemaCache.updateLastCache(seriesPath1, timeValuePair2, false, 100L); + TimeValuePair cachedTimeValuePair2 = dataNodeSchemaCache.getLastCache(seriesPath1); + Assert.assertNotNull(cachedTimeValuePair2); + Assert.assertEquals(timestamp, cachedTimeValuePair2.getTimestamp()); + Assert.assertEquals(value, cachedTimeValuePair2.getValue()); + Assert.assertNull(dataNodeSchemaCache.getLastCache(seriesPath2)); + Assert.assertNull(dataNodeSchemaCache.getLastCache(seriesPath3)); + + // same time but high priority + dataNodeSchemaCache.updateLastCache(seriesPath1, timeValuePair2, true, 100L); + cachedTimeValuePair2 = dataNodeSchemaCache.getLastCache(seriesPath1); + Assert.assertNotNull(cachedTimeValuePair2); + Assert.assertEquals(timestamp, cachedTimeValuePair2.getTimestamp()); + Assert.assertEquals(value2, cachedTimeValuePair2.getValue()); + Assert.assertNull(dataNodeSchemaCache.getLastCache(seriesPath2)); + Assert.assertNull(dataNodeSchemaCache.getLastCache(seriesPath3)); + + // put into last cache when cache already exist + TimeValuePair timeValuePair3 = new TimeValuePair(timestamp2, value3); + dataNodeSchemaCache.updateLastCache(seriesPath1, timeValuePair3, false, 100L); + TimeValuePair cachedTimeValuePair3 = dataNodeSchemaCache.getLastCache(seriesPath1); + Assert.assertNotNull(cachedTimeValuePair3); + Assert.assertEquals(timestamp2, cachedTimeValuePair3.getTimestamp()); + Assert.assertEquals(value3, cachedTimeValuePair3.getValue()); + Assert.assertNull(dataNodeSchemaCache.getLastCache(seriesPath2)); + Assert.assertNull(dataNodeSchemaCache.getLastCache(seriesPath3)); + + // invalid cache + dataNodeSchemaCache.invalidate(seriesPath1); + Assert.assertNull(dataNodeSchemaCache.getLastCache(seriesPath1)); + Assert.assertNull(dataNodeSchemaCache.getLastCache(seriesPath2)); + Assert.assertNull(dataNodeSchemaCache.getLastCache(seriesPath3)); + } + private SchemaTree generateSchemaTree1() throws IllegalPathException { SchemaTree schemaTree = new SchemaTree(); From e108016b528c45d7877f709e03c73cee409e779c Mon Sep 17 00:00:00 2001 From: ZhangHongYin <46039728+SpriCoder@users.noreply.github.com> Date: Tue, 17 May 2022 11:55:50 +0800 Subject: [PATCH 028/436] remove enable_performance_stat in iotdb-engine.properties, and add enablePerformanceStat in iotdb-metric.yml (#5920) --- .../src/main/assembly/resources/conf/iotdb-metric.yml | 3 +++ .../org/apache/iotdb/metrics/config/MetricConfig.java | 11 +++++++++++ .../assembly/resources/conf/iotdb-engine.properties | 4 ---- .../java/org/apache/iotdb/db/conf/IoTDBConfig.java | 11 ----------- .../org/apache/iotdb/db/conf/IoTDBDescriptor.java | 7 ------- .../service/thrift/impl/DataNodeTSIServiceImpl.java | 3 ++- .../iotdb/db/service/thrift/impl/TSServiceImpl.java | 3 ++- 7 files changed, 18 insertions(+), 24 deletions(-) diff --git a/metrics/interface/src/main/assembly/resources/conf/iotdb-metric.yml b/metrics/interface/src/main/assembly/resources/conf/iotdb-metric.yml index 53e9bef1947a..39c498747a28 100644 --- a/metrics/interface/src/main/assembly/resources/conf/iotdb-metric.yml +++ b/metrics/interface/src/main/assembly/resources/conf/iotdb-metric.yml @@ -20,6 +20,9 @@ # whether enable the module enableMetric: false +# Is stat performance of sub-module enable +enablePerformanceStat: false + # Multiple reporter, options: [JMX, PROMETHEUS, IOTDB], IOTDB is off by default metricReporterList: - JMX diff --git a/metrics/interface/src/main/java/org/apache/iotdb/metrics/config/MetricConfig.java b/metrics/interface/src/main/java/org/apache/iotdb/metrics/config/MetricConfig.java index 5a00a05cfe97..2c8444b7f3db 100644 --- a/metrics/interface/src/main/java/org/apache/iotdb/metrics/config/MetricConfig.java +++ b/metrics/interface/src/main/java/org/apache/iotdb/metrics/config/MetricConfig.java @@ -33,6 +33,9 @@ public class MetricConfig { /** enable publishing data. */ private Boolean enableMetric = false; + /** Is stat performance of sub-module enable */ + private Boolean enablePerformanceStat = false; + /** The of monitor frame */ private MonitorType monitorType = MonitorType.MICROMETER; @@ -168,6 +171,14 @@ public void setEnableMetric(Boolean enableMetric) { this.enableMetric = enableMetric; } + public Boolean getEnablePerformanceStat() { + return enablePerformanceStat; + } + + public void setEnablePerformanceStat(Boolean enablePerformanceStat) { + this.enablePerformanceStat = enablePerformanceStat; + } + public MonitorType getMonitorType() { return monitorType; } diff --git a/server/src/assembly/resources/conf/iotdb-engine.properties b/server/src/assembly/resources/conf/iotdb-engine.properties index 3235dd92e4a4..67c77c150caf 100644 --- a/server/src/assembly/resources/conf/iotdb-engine.properties +++ b/server/src/assembly/resources/conf/iotdb-engine.properties @@ -654,10 +654,6 @@ timestamp_precision=ms ### performance statistic configuration #################### -# Is stat performance of sub-module enable -# Datatype: boolean -# enable_performance_stat=false - # Uncomment following fields to configure the tracing root directory. # For Window platform, the index is as follows: # tracing_dir=data\\tracing diff --git a/server/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java b/server/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java index 8cf84cf10c33..1177b9940f3b 100644 --- a/server/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java +++ b/server/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java @@ -508,9 +508,6 @@ public class IoTDBConfig { /** Replace implementation class of influxdb protocol service */ private String influxdbImplClassName = InfluxDBServiceImpl.class.getName(); - /** Is stat performance of sub-module enable. */ - private boolean enablePerformanceStat = false; - /** whether use chunkBufferPool. */ private boolean chunkBufferPoolEnable = false; @@ -1605,14 +1602,6 @@ void setExternalSortThreshold(int externalSortThreshold) { this.externalSortThreshold = externalSortThreshold; } - public boolean isEnablePerformanceStat() { - return enablePerformanceStat; - } - - public void setEnablePerformanceStat(boolean enablePerformanceStat) { - this.enablePerformanceStat = enablePerformanceStat; - } - public boolean isEnablePartialInsert() { return enablePartialInsert; } diff --git a/server/src/main/java/org/apache/iotdb/db/conf/IoTDBDescriptor.java b/server/src/main/java/org/apache/iotdb/db/conf/IoTDBDescriptor.java index 40b680bfed5b..206484f2b8ef 100644 --- a/server/src/main/java/org/apache/iotdb/db/conf/IoTDBDescriptor.java +++ b/server/src/main/java/org/apache/iotdb/db/conf/IoTDBDescriptor.java @@ -593,13 +593,6 @@ private void loadProps() { properties.getProperty( "enable_partial_insert", String.valueOf(conf.isEnablePartialInsert())))); - conf.setEnablePerformanceStat( - Boolean.parseBoolean( - properties - .getProperty( - "enable_performance_stat", Boolean.toString(conf.isEnablePerformanceStat())) - .trim())); - int maxConcurrentClientNum = Integer.parseInt( properties.getProperty( diff --git a/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/DataNodeTSIServiceImpl.java b/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/DataNodeTSIServiceImpl.java index c86f88b097d9..10fe232ed859 100644 --- a/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/DataNodeTSIServiceImpl.java +++ b/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/DataNodeTSIServiceImpl.java @@ -53,6 +53,7 @@ import org.apache.iotdb.db.service.metrics.MetricsService; import org.apache.iotdb.db.service.metrics.Operation; import org.apache.iotdb.db.utils.QueryDataSetUtils; +import org.apache.iotdb.metrics.config.MetricConfigDescriptor; import org.apache.iotdb.metrics.utils.MetricLevel; import org.apache.iotdb.rpc.RpcUtils; import org.apache.iotdb.rpc.TSStatusCode; @@ -1059,7 +1060,7 @@ private TSStatus getNotLoggedInStatus() { /** Add stat of operation into metrics */ private void addOperationLatency(Operation operation, long startTime) { - if (CONFIG.isEnablePerformanceStat()) { + if (MetricConfigDescriptor.getInstance().getMetricConfig().getEnablePerformanceStat()) { MetricsService.getInstance() .getMetricManager() .histogram( diff --git a/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/TSServiceImpl.java b/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/TSServiceImpl.java index a1b53171469d..febb2e195582 100644 --- a/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/TSServiceImpl.java +++ b/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/TSServiceImpl.java @@ -76,6 +76,7 @@ import org.apache.iotdb.db.tools.watermark.GroupedLSBWatermarkEncoder; import org.apache.iotdb.db.tools.watermark.WatermarkEncoder; import org.apache.iotdb.db.utils.QueryDataSetUtils; +import org.apache.iotdb.metrics.config.MetricConfigDescriptor; import org.apache.iotdb.metrics.utils.MetricLevel; import org.apache.iotdb.rpc.RedirectException; import org.apache.iotdb.rpc.RpcUtils; @@ -2074,7 +2075,7 @@ private TSStatus getNotLoggedInStatus() { /** Add stat of operation into metrics */ private void addOperationLatency(Operation operation, long startTime) { - if (CONFIG.isEnablePerformanceStat()) { + if (MetricConfigDescriptor.getInstance().getMetricConfig().getEnablePerformanceStat()) { MetricsService.getInstance() .getMetricManager() .histogram( From 5032057a367d4b8acdb1d60f2721d7af69e703d1 Mon Sep 17 00:00:00 2001 From: Haonan Date: Tue, 17 May 2022 13:32:54 +0800 Subject: [PATCH 029/436] [IOTDB-3201] Support RowDataQuery API for new cluster (#5922) --- .../mpp/plan/parser/StatementGenerator.java | 39 +++++-------- .../thrift/impl/DataNodeTSIServiceImpl.java | 57 ++++++++++++++++++- 2 files changed, 71 insertions(+), 25 deletions(-) diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/StatementGenerator.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/StatementGenerator.java index faeb37ef7b41..c5428151588e 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/StatementGenerator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/StatementGenerator.java @@ -23,7 +23,6 @@ import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.exception.query.QueryProcessException; import org.apache.iotdb.db.mpp.common.filter.BasicFunctionFilter; -import org.apache.iotdb.db.mpp.common.filter.QueryFilter; import org.apache.iotdb.db.mpp.plan.constant.FilterConstant; import org.apache.iotdb.db.mpp.plan.statement.Statement; import org.apache.iotdb.db.mpp.plan.statement.component.FromComponent; @@ -44,7 +43,12 @@ import org.apache.iotdb.db.qp.sql.IoTDBSqlParser; import org.apache.iotdb.db.qp.sql.SqlLexer; import org.apache.iotdb.db.qp.strategy.SQLParseError; +import org.apache.iotdb.db.query.expression.binary.GreaterEqualExpression; +import org.apache.iotdb.db.query.expression.binary.LessThanExpression; +import org.apache.iotdb.db.query.expression.binary.LogicAndExpression; +import org.apache.iotdb.db.query.expression.leaf.ConstantOperand; import org.apache.iotdb.db.query.expression.leaf.TimeSeriesOperand; +import org.apache.iotdb.db.query.expression.leaf.TimestampOperand; import org.apache.iotdb.db.utils.QueryDataSetUtils; import org.apache.iotdb.service.rpc.thrift.TSCreateAlignedTimeseriesReq; import org.apache.iotdb.service.rpc.thrift.TSCreateMultiTimeseriesReq; @@ -71,9 +75,7 @@ import java.time.ZoneId; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; -import java.util.Set; import static org.apache.iotdb.commons.conf.IoTDBConstant.TIME; @@ -100,27 +102,16 @@ public static Statement createStatement(TSRawDataQueryReq rawDataQueryReq, ZoneI selectComponent.addResultColumn(new ResultColumn(new TimeSeriesOperand(new PartialPath("")))); // set query filter - QueryFilter queryFilter = new QueryFilter(FilterConstant.FilterType.KW_AND); - PartialPath timePath = new PartialPath(TIME); - queryFilter.setSinglePath(timePath); - Set pathSet = new HashSet<>(); - pathSet.add(timePath); - queryFilter.setIsSingle(true); - queryFilter.setPathSet(pathSet); - - BasicFunctionFilter left = - new BasicFunctionFilter( - FilterConstant.FilterType.GREATERTHANOREQUALTO, - timePath, - Long.toString(rawDataQueryReq.getStartTime())); - BasicFunctionFilter right = - new BasicFunctionFilter( - FilterConstant.FilterType.LESSTHAN, - timePath, - Long.toString(rawDataQueryReq.getEndTime())); - queryFilter.addChildOperator(left); - queryFilter.addChildOperator(right); - // whereCondition.setQueryFilter(queryFilter); + GreaterEqualExpression leftPredicate = + new GreaterEqualExpression( + new TimestampOperand(), + new ConstantOperand(TSDataType.INT64, Long.toString(rawDataQueryReq.getStartTime()))); + LessThanExpression rightPredicate = + new LessThanExpression( + new TimestampOperand(), + new ConstantOperand(TSDataType.INT64, Long.toString(rawDataQueryReq.getEndTime()))); + LogicAndExpression predicate = new LogicAndExpression(leftPredicate, rightPredicate); + whereCondition.setPredicate(predicate); queryStatement.setSelectComponent(selectComponent); queryStatement.setFromComponent(fromComponent); diff --git a/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/DataNodeTSIServiceImpl.java b/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/DataNodeTSIServiceImpl.java index 10fe232ed859..4d403bef8d42 100644 --- a/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/DataNodeTSIServiceImpl.java +++ b/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/DataNodeTSIServiceImpl.java @@ -949,7 +949,62 @@ public TSStatus deleteData(TSDeleteDataReq req) { @Override public TSExecuteStatementResp executeRawDataQuery(TSRawDataQueryReq req) { - throw new UnsupportedOperationException(); + if (!SESSION_MANAGER.checkLogin(req.getSessionId())) { + return RpcUtils.getTSExecuteStatementResp(getNotLoggedInStatus()); + } + long startTime = System.currentTimeMillis(); + try { + Statement s = + StatementGenerator.createStatement(req, SESSION_MANAGER.getZoneId(req.getSessionId())); + + // permission check + TSStatus status = AuthorityChecker.checkAuthority(s, req.sessionId); + if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { + return RpcUtils.getTSExecuteStatementResp(status); + } + + QUERY_FREQUENCY_RECORDER.incrementAndGet(); + AUDIT_LOGGER.debug("Session {} execute Row Data Query: {}", req.sessionId, req); + long queryId = SESSION_MANAGER.requestQueryId(req.statementId, true); + QueryId id = new QueryId(String.valueOf(queryId)); + // create and cache dataset + ExecutionResult result = + COORDINATOR.execute( + s, + id, + SESSION_MANAGER.getSessionInfo(req.sessionId), + "", + PARTITION_FETCHER, + SCHEMA_FETCHER); + + if (result.status.code != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { + throw new RuntimeException("error code: " + result.status); + } + + IQueryExecution queryExecution = COORDINATOR.getQueryExecution(id); + + TSExecuteStatementResp resp; + if (queryExecution.isQuery()) { + resp = createResponse(queryExecution.getDatasetHeader(), queryId); + resp.setStatus(result.status); + resp.setQueryDataSet( + QueryDataSetUtils.convertTsBlockByFetchSize(queryExecution, req.fetchSize)); + } else { + resp = RpcUtils.getTSExecuteStatementResp(result.status); + } + + return resp; + } catch (Exception e) { + // TODO call the coordinator to release query resource + return RpcUtils.getTSExecuteStatementResp( + onQueryException(e, "\"" + req + "\". " + OperationType.EXECUTE_RAW_DATA_QUERY)); + } finally { + addOperationLatency(Operation.EXECUTE_QUERY, startTime); + long costTime = System.currentTimeMillis() - startTime; + if (costTime >= CONFIG.getSlowQueryThreshold()) { + SLOW_SQL_LOGGER.info("Cost: {} ms, sql is {}", costTime, req); + } + } } @Override From c76113ac456cb4ea7f3b1c7e45d488ea381064e7 Mon Sep 17 00:00:00 2001 From: SzyWilliam <48054931+SzyWilliam@users.noreply.github.com> Date: Tue, 17 May 2022 15:58:07 +0800 Subject: [PATCH 030/436] BugFix: Delete snapshotDir on failure (#5927) * delete snapshotDir on failure * spotless --- .../consensus/ratis/ApplicationStateMachineProxy.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/consensus/src/main/java/org/apache/iotdb/consensus/ratis/ApplicationStateMachineProxy.java b/consensus/src/main/java/org/apache/iotdb/consensus/ratis/ApplicationStateMachineProxy.java index 3617a36b0c98..ab0f65fa3865 100644 --- a/consensus/src/main/java/org/apache/iotdb/consensus/ratis/ApplicationStateMachineProxy.java +++ b/consensus/src/main/java/org/apache/iotdb/consensus/ratis/ApplicationStateMachineProxy.java @@ -37,6 +37,7 @@ import org.apache.ratis.statemachine.StateMachineStorage; import org.apache.ratis.statemachine.TransactionContext; import org.apache.ratis.statemachine.impl.BaseStateMachine; +import org.apache.ratis.util.FileUtils; import org.apache.ratis.util.LifeCycle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -148,6 +149,14 @@ public long takeSnapshot() throws IOException { } boolean success = applicationStateMachine.takeSnapshot(snapshotDir); if (!success) { + // statemachine is supposed to clear snapshotDir on failure + boolean isEmpty = snapshotDir.delete(); + if (!isEmpty) { + logger.warn( + "StateMachine take snapshot failed but leave unexpected remaining files at " + + snapshotDir.getAbsolutePath()); + FileUtils.deleteFully(snapshotDir); + } return RaftLog.INVALID_LOG_INDEX; } return lastApplied.getIndex(); From b0d58269b3ac5d806e50563a5777a5898a503aad Mon Sep 17 00:00:00 2001 From: Marcos_Zyk <38524330+MarcosZyk@users.noreply.github.com> Date: Tue, 17 May 2022 19:54:13 +0800 Subject: [PATCH 031/436] [IOTDB-3210]fix npe (#5932) --- .../org/apache/iotdb/db/service/IoTDBShutdownHook.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/server/src/main/java/org/apache/iotdb/db/service/IoTDBShutdownHook.java b/server/src/main/java/org/apache/iotdb/db/service/IoTDBShutdownHook.java index cbd45bf9ecd5..a3d9ee2ae5b3 100644 --- a/server/src/main/java/org/apache/iotdb/db/service/IoTDBShutdownHook.java +++ b/server/src/main/java/org/apache/iotdb/db/service/IoTDBShutdownHook.java @@ -18,8 +18,10 @@ */ package org.apache.iotdb.db.service; +import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.engine.StorageEngine; import org.apache.iotdb.db.engine.compaction.CompactionTaskManager; +import org.apache.iotdb.db.metadata.schemaregion.SchemaEngineMode; import org.apache.iotdb.db.utils.MemUtils; import org.apache.iotdb.db.wal.WALManager; @@ -34,7 +36,10 @@ public class IoTDBShutdownHook extends Thread { public void run() { CompactionTaskManager.getInstance().stop(); // close rocksdb if possible to avoid lose data - IoTDB.configManager.clear(); + if (SchemaEngineMode.valueOf(IoTDBDescriptor.getInstance().getConfig().getSchemaEngineMode()) + .equals(SchemaEngineMode.Rocksdb_based)) { + IoTDB.configManager.clear(); + } // == flush data to Tsfile and remove WAL log files StorageEngine.getInstance().syncCloseAllProcessor(); From c0529f57aaca17297413d7811a0617baf821086a Mon Sep 17 00:00:00 2001 From: Mrquan <50790061+MrQuansy@users.noreply.github.com> Date: Tue, 17 May 2022 20:50:31 +0800 Subject: [PATCH 032/436] fix a bug for system.properties (#5933) --- .../org/apache/iotdb/db/client/ConfigNodeInfo.java | 4 +++- .../org/apache/iotdb/db/conf/IoTDBStartCheck.java | 13 +++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/server/src/main/java/org/apache/iotdb/db/client/ConfigNodeInfo.java b/server/src/main/java/org/apache/iotdb/db/client/ConfigNodeInfo.java index e98efb15ec38..85fd29516fa2 100644 --- a/server/src/main/java/org/apache/iotdb/db/client/ConfigNodeInfo.java +++ b/server/src/main/java/org/apache/iotdb/db/client/ConfigNodeInfo.java @@ -107,7 +107,9 @@ private void storeConfigNode() throws IOException { } properties.setProperty( CONFIG_NODE_LIST, NodeUrlUtils.convertTEndPointUrls(new ArrayList<>(onlineConfigNodes))); - properties.store(new FileOutputStream(propertiesFile), ""); + try (FileOutputStream fileOutputStream = new FileOutputStream(propertiesFile)) { + properties.store(fileOutputStream, ""); + } } public void loadConfigNodeList() { diff --git a/server/src/main/java/org/apache/iotdb/db/conf/IoTDBStartCheck.java b/server/src/main/java/org/apache/iotdb/db/conf/IoTDBStartCheck.java index 533ce1858666..c7e9be67e31b 100644 --- a/server/src/main/java/org/apache/iotdb/db/conf/IoTDBStartCheck.java +++ b/server/src/main/java/org/apache/iotdb/db/conf/IoTDBStartCheck.java @@ -108,8 +108,6 @@ public class IoTDBStartCheck { private static final String DATA_NODE_ID = "data_node_id"; - private static final String CONFIG_NODE_LIST = "config_node_list"; - private static final String IOTDB_VERSION_STRING = "iotdb_version"; public static IoTDBStartCheck getInstance() { @@ -390,6 +388,15 @@ private void throwException(String parameter, Object badValue) throws Configurat parameter, String.valueOf(badValue), properties.getProperty(parameter)); } + // reload properties from system.properties + private void reloadProperties() throws IOException { + try (FileInputStream inputStream = new FileInputStream(propertiesFile); + InputStreamReader inputStreamReader = + new InputStreamReader(inputStream, TSFileConfig.STRING_CHARSET)) { + properties.load(inputStreamReader); + } + } + /** call this method to serialize DataNodeId */ public void serializeDataNodeId(int dataNodeId) throws IOException { // create an empty tmpPropertiesFile @@ -400,6 +407,8 @@ public void serializeDataNodeId(int dataNodeId) throws IOException { System.exit(-1); } + reloadProperties(); + try (FileOutputStream tmpFOS = new FileOutputStream(tmpPropertiesFile.toString())) { properties.setProperty(DATA_NODE_ID, String.valueOf(dataNodeId)); properties.store(tmpFOS, SYSTEM_PROPERTIES_STRING); From 7042883fc0334a75ba7a674c967eb5b70f0273ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=AD=A3=E6=98=8E?= <876670773@qq.com> Date: Tue, 17 May 2022 21:31:14 +0800 Subject: [PATCH 033/436] [IOTDB-3219] fix stop-server on windows (#5936) * fix stop-server on windows * fix stop-server.bat on cluster-confignode --- confignode/src/assembly/resources/sbin/stop-confignode.bat | 2 +- server/src/assembly/resources/sbin/stop-server.bat | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/confignode/src/assembly/resources/sbin/stop-confignode.bat b/confignode/src/assembly/resources/sbin/stop-confignode.bat index 9771f475ad00..bcfc5d864303 100644 --- a/confignode/src/assembly/resources/sbin/stop-confignode.bat +++ b/confignode/src/assembly/resources/sbin/stop-confignode.bat @@ -22,7 +22,7 @@ set current_dir=%~dp0 set superior_dir=%current_dir%\..\ -for /f "eol=; tokens=2,2 delims==" %%i in ('findstr /i "rpc_port" +for /f "eol=; tokens=2,2 delims==" %%i in ('findstr /i "^rpc_port" %superior_dir%\conf\iotdb-confignode.properties') do ( set rpc_port=%%i ) diff --git a/server/src/assembly/resources/sbin/stop-server.bat b/server/src/assembly/resources/sbin/stop-server.bat index b1dbf7cf83b4..a2f85581fae7 100755 --- a/server/src/assembly/resources/sbin/stop-server.bat +++ b/server/src/assembly/resources/sbin/stop-server.bat @@ -22,7 +22,7 @@ set current_dir=%~dp0 set superior_dir=%current_dir%\..\ -for /f "eol=; tokens=2,2 delims==" %%i in ('findstr /i "rpc_port" +for /f "eol=; tokens=2,2 delims==" %%i in ('findstr /i "^rpc_port" %superior_dir%\conf\iotdb-engine.properties') do ( set rpc_port=%%i ) From b8cd0c2cc09912e5e12bd783503a428f7987b044 Mon Sep 17 00:00:00 2001 From: Haonan Date: Tue, 17 May 2022 22:52:29 +0800 Subject: [PATCH 034/436] Avoild using symlink when pack all-in-one package (#5935) --- cli/src/assembly/resources/sbin/start-cli.bat | 2 +- cli/src/assembly/resources/sbin/start-cli.sh | 8 +- .../resources/sbin/start-confignode.bat | 2 +- .../resources/sbin/start-confignode.sh | 8 +- distribution/pom.xml | 22 ----- distribution/src/assembly/cluster.xml | 81 ------------------- .../resources/sbin/remove-datanode.bat | 2 +- .../resources/sbin/remove-datanode.sh | 8 +- .../resources/sbin/start-datanode.bat | 2 +- .../assembly/resources/sbin/start-datanode.sh | 8 +- .../assembly/resources/sbin/start-server.bat | 2 +- .../assembly/resources/sbin/start-server.sh | 8 +- 12 files changed, 40 insertions(+), 113 deletions(-) delete mode 100644 distribution/src/assembly/cluster.xml diff --git a/cli/src/assembly/resources/sbin/start-cli.bat b/cli/src/assembly/resources/sbin/start-cli.bat index 21bb4000c522..cbd375e6f2b7 100644 --- a/cli/src/assembly/resources/sbin/start-cli.bat +++ b/cli/src/assembly/resources/sbin/start-cli.bat @@ -37,7 +37,7 @@ set JAVA_OPTS=-ea^ -DIOTDB_HOME="%IOTDB_HOME%" REM For each jar in the IOTDB_HOME lib directory call append to build the CLASSPATH variable. -set CLASSPATH="%IOTDB_HOME%\lib\*" +if EXIST %IOTDB_HOME%\lib (set CLASSPATH="%IOTDB_HOME%\lib\*") else set CLASSPATH="%IOTDB_HOME%\..\lib\*" REM ----------------------------------------------------------------------------- set PARAMETERS=%* diff --git a/cli/src/assembly/resources/sbin/start-cli.sh b/cli/src/assembly/resources/sbin/start-cli.sh index 20fb4506a027..dbeedc725059 100644 --- a/cli/src/assembly/resources/sbin/start-cli.sh +++ b/cli/src/assembly/resources/sbin/start-cli.sh @@ -30,8 +30,14 @@ IOTDB_CLI_CONF=${IOTDB_HOME}/conf MAIN_CLASS=org.apache.iotdb.cli.Cli +if [ -d ${IOTDB_HOME}/lib ]; then +LIB_PATH=${IOTDB_HOME}/lib +else +LIB_PATH=${IOTDB_HOME}/../lib +fi + CLASSPATH="" -for f in ${IOTDB_HOME}/lib/*.jar; do +for f in ${LIB_PATH}/*.jar; do CLASSPATH=${CLASSPATH}":"$f done diff --git a/confignode/src/assembly/resources/sbin/start-confignode.bat b/confignode/src/assembly/resources/sbin/start-confignode.bat index 62ff64ec96d9..65db4c98029f 100644 --- a/confignode/src/assembly/resources/sbin/start-confignode.bat +++ b/confignode/src/assembly/resources/sbin/start-confignode.bat @@ -93,7 +93,7 @@ set JAVA_OPTS=-ea^ @REM ***** CLASSPATH library setting ***** @REM Ensure that any user defined CLASSPATH variables are not used on startup -set CLASSPATH="%CONFIGNODE_HOME%\lib\*" +if EXIST %IOTDB_HOME%\lib (set CLASSPATH="%IOTDB_HOME%\lib\*") else set CLASSPATH="%IOTDB_HOME%\..\lib\*" set CLASSPATH=%CLASSPATH%;iotdb.ConfigNode goto okClasspath diff --git a/confignode/src/assembly/resources/sbin/start-confignode.sh b/confignode/src/assembly/resources/sbin/start-confignode.sh index dffd31f5cd92..fd51caa12828 100644 --- a/confignode/src/assembly/resources/sbin/start-confignode.sh +++ b/confignode/src/assembly/resources/sbin/start-confignode.sh @@ -57,8 +57,14 @@ else echo "can't find $CONFIGNODE_CONF/confignode-env.sh" fi +if [ -d ${IOTDB_HOME}/lib ]; then +LIB_PATH=${IOTDB_HOME}/lib +else +LIB_PATH=${IOTDB_HOME}/../lib +fi + CLASSPATH="" -for f in ${CONFIGNODE_HOME}/lib/*.jar; do +for f in ${LIB_PATH}/*.jar; do CLASSPATH=${CLASSPATH}":"$f done classname=org.apache.iotdb.confignode.service.ConfigNode diff --git a/distribution/pom.xml b/distribution/pom.xml index 327bb2e8b005..c9bd405b0de2 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -32,28 +32,6 @@ - - org.apache.maven.plugins - maven-antrun-plugin - 3.0.0 - - - package - - - - - - - - - - - run - - - - org.apache.maven.plugins maven-assembly-plugin diff --git a/distribution/src/assembly/cluster.xml b/distribution/src/assembly/cluster.xml deleted file mode 100644 index 7f5903cd945b..000000000000 --- a/distribution/src/assembly/cluster.xml +++ /dev/null @@ -1,81 +0,0 @@ - - - - cluster-bin - - dir - zip - - apache-iotdb-${project.version}-cluster-bin - - - - *:iotdb-cluster:zip:* - *:iotdb-cli:zip:* - - ${file.separator} - ${artifact.artifactId}.${artifact.extension} - true - - - - - - - - - ${maven.multiModuleProjectDirectory}/cluster/src/assembly/resources/sbin - sbin - 0755 - - - - - - - ${maven.multiModuleProjectDirectory}/server/src/assembly/resources/tools - tools - 0755 - - - - ${maven.multiModuleProjectDirectory}/cli/src/assembly/resources/sbin - sbin - 0755 - - - ${maven.multiModuleProjectDirectory}/cli/src/assembly/resources/tools - tools - 0755 - - - - - ${maven.multiModuleProjectDirectory}/server/src/assembly/resources/conf/iotdb-env.sh - conf/iotdb-env.sh - 0755 - - - - common-files.xml - - diff --git a/server/src/assembly/resources/sbin/remove-datanode.bat b/server/src/assembly/resources/sbin/remove-datanode.bat index 45e18b375b06..d15734775607 100644 --- a/server/src/assembly/resources/sbin/remove-datanode.bat +++ b/server/src/assembly/resources/sbin/remove-datanode.bat @@ -77,7 +77,7 @@ set JAVA_OPTS=-ea^ @REM ***** CLASSPATH library setting ***** @REM Ensure that any user defined CLASSPATH variables are not used on startup -set CLASSPATH="%IOTDB_HOME%\lib" +if EXIST %IOTDB_HOME%\lib (set CLASSPATH="%IOTDB_HOME%\lib\*") else set CLASSPATH="%IOTDB_HOME%\..\lib\*" @REM For each jar in the IOTDB_HOME lib directory call append to build the CLASSPATH variable. set CLASSPATH=%CLASSPATH%;"%IOTDB_HOME%\lib\*" diff --git a/server/src/assembly/resources/sbin/remove-datanode.sh b/server/src/assembly/resources/sbin/remove-datanode.sh index 350e012d4874..8804edb17330 100644 --- a/server/src/assembly/resources/sbin/remove-datanode.sh +++ b/server/src/assembly/resources/sbin/remove-datanode.sh @@ -46,8 +46,14 @@ if [ -z $JAVA ] ; then exit 1; fi +if [ -d ${IOTDB_HOME}/lib ]; then +LIB_PATH=${IOTDB_HOME}/lib +else +LIB_PATH=${IOTDB_HOME}/../lib +fi + CLASSPATH="" -for f in ${IOTDB_HOME}/lib/*.jar; do +for f in ${LIB_PATH}/*.jar; do CLASSPATH=${CLASSPATH}":"$f done classname=org.apache.iotdb.db.service.DataNode diff --git a/server/src/assembly/resources/sbin/start-datanode.bat b/server/src/assembly/resources/sbin/start-datanode.bat index f3c737c5dcff..4df45ed56c4d 100755 --- a/server/src/assembly/resources/sbin/start-datanode.bat +++ b/server/src/assembly/resources/sbin/start-datanode.bat @@ -105,7 +105,7 @@ set JAVA_OPTS=-ea^ @REM ***** CLASSPATH library setting ***** @REM Ensure that any user defined CLASSPATH variables are not used on startup -set CLASSPATH="%IOTDB_HOME%\lib\*" +if EXIST %IOTDB_HOME%\lib (set CLASSPATH="%IOTDB_HOME%\lib\*") else set CLASSPATH="%IOTDB_HOME%\..\lib\*" @REM this special suffix 'iotdb.DataNode' is mandatory as stop-node.bat uses it to filter the process id. set CLASSPATH=%CLASSPATH%;iotdb.DataNode diff --git a/server/src/assembly/resources/sbin/start-datanode.sh b/server/src/assembly/resources/sbin/start-datanode.sh index 63bb4f835dc6..770e4ff4906d 100755 --- a/server/src/assembly/resources/sbin/start-datanode.sh +++ b/server/src/assembly/resources/sbin/start-datanode.sh @@ -72,8 +72,14 @@ if [ -z $JAVA ] ; then exit 1; fi +if [ -d ${IOTDB_HOME}/lib ]; then +LIB_PATH=${IOTDB_HOME}/lib +else +LIB_PATH=${IOTDB_HOME}/../lib +fi + CLASSPATH="" -for f in ${IOTDB_HOME}/lib/*.jar; do +for f in ${LIB_PATH}/*.jar; do CLASSPATH=${CLASSPATH}":"$f done classname=org.apache.iotdb.db.service.DataNode diff --git a/server/src/assembly/resources/sbin/start-server.bat b/server/src/assembly/resources/sbin/start-server.bat index 2c07383758a2..0e7c89260a9f 100755 --- a/server/src/assembly/resources/sbin/start-server.bat +++ b/server/src/assembly/resources/sbin/start-server.bat @@ -96,7 +96,7 @@ set JAVA_OPTS=-ea^ @REM ***** CLASSPATH library setting ***** @REM Ensure that any user defined CLASSPATH variables are not used on startup -set CLASSPATH="%IOTDB_HOME%\lib\*" +if EXIST %IOTDB_HOME%\lib (set CLASSPATH="%IOTDB_HOME%\lib\*") else set CLASSPATH="%IOTDB_HOME%\..\lib\*" set CLASSPATH=%CLASSPATH%;iotdb.IoTDB goto okClasspath diff --git a/server/src/assembly/resources/sbin/start-server.sh b/server/src/assembly/resources/sbin/start-server.sh index c1c44f3fbc35..d352e8ba41fe 100755 --- a/server/src/assembly/resources/sbin/start-server.sh +++ b/server/src/assembly/resources/sbin/start-server.sh @@ -57,8 +57,14 @@ else echo "can't find $IOTDB_CONF/iotdb-env.sh" fi +if [ -d ${IOTDB_HOME}/lib ]; then +LIB_PATH=${IOTDB_HOME}/lib +else +LIB_PATH=${IOTDB_HOME}/../lib +fi + CLASSPATH="" -for f in ${IOTDB_HOME}/lib/*.jar; do +for f in ${LIB_PATH}/*.jar; do CLASSPATH=${CLASSPATH}":"$f done classname=org.apache.iotdb.db.service.IoTDB From f4e458178ce09b2b3f3894ecde4887dcd52efb6f Mon Sep 17 00:00:00 2001 From: Liao Lanyu <48237151+Plutooooooo@users.noreply.github.com> Date: Tue, 17 May 2022 23:48:09 +0800 Subject: [PATCH 035/436] [IOTDB-3073][IOTDB-3095] Unescape identifier and Use antlr to parse PartialPath/Path (#5848) --- antlr/pom.xml | 1 + .../iotdb/db/qp/sql/IdentifierParser.g4 | 179 ++++++ .../iotdb/db/qp/sql/InfluxDBSqlParser.g4 | 14 +- .../apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 | 9 +- .../org/apache/iotdb/db/qp/sql/PathParser.g4 | 52 ++ .../org/apache/iotdb/db/qp/sql/SqlLexer.g4 | 4 +- client-py/tests/test_dataframe.py | 10 +- .../client/sync/SyncClientAdaptorTest.java | 2 +- .../persistence/ClusterSchemaInfoTest.java | 2 +- .../integration/IoTDBContinuousQueryIT.java | 42 ++ .../integration/IoTDBCreateTimeseriesIT.java | 2 +- .../db/integration/IoTDBQuotedPathIT.java | 2 +- .../db/integration/IoTDBSelectIntoIT.java | 2 +- .../db/integration/IoTDBSimpleQueryIT.java | 4 +- .../IoTDBSyntaxConventionIdentifierIT.java | 160 +++++- .../IoTDBWithoutNullAnyFilterIT.java | 10 +- .../iotdb/session/IoTDBSessionComplexIT.java | 8 +- .../iotdb/session/IoTDBSessionSimpleIT.java | 8 +- .../IoTDBSessionSyntaxConventionIT.java | 18 +- .../org/apache/iotdb/session/SessionTest.java | 28 +- .../iotdb/session/template/TemplateUT.java | 14 +- node-commons/pom.xml | 5 + .../iotdb/commons/path/PartialPath.java | 51 +- .../apache/iotdb/commons/utils/PathUtils.java | 78 +-- .../iotdb/commons/path/PartialPathTest.java | 511 ++++++++++++++++++ .../iotdb/commons/utils/PathUtilsTest.java | 125 ----- .../schemaregion/rocksdb/RSchemaRegion.java | 2 +- .../schemaregion/rocksdb/RSchemaUtils.java | 2 +- server/pom.xml | 5 - .../org/apache/iotdb/db/conf/IoTDBConfig.java | 2 +- .../db/engine/cq/ContinuousQueryTask.java | 13 +- .../metadata/idtable/IDTableHashmapImpl.java | 11 +- .../idtable/entry/SHA256DeviceID.java | 7 +- .../mtree/MTreeBelowSGCachedImpl.java | 4 +- .../mtree/MTreeBelowSGMemoryImpl.java | 4 +- .../store/disk/schemafile/SchemaFile.java | 2 +- .../iotdb/db/metadata/path/AlignedPath.java | 13 +- .../iotdb/db/metadata/template/Template.java | 20 +- .../iotdb/db/mpp/plan/parser/ASTVisitor.java | 40 +- .../mpp/plan/parser/StatementGenerator.java | 8 +- .../db/qp/physical/sys/SetTemplatePlan.java | 2 +- .../db/qp/physical/sys/UnsetTemplatePlan.java | 2 +- .../iotdb/db/qp/sql/IoTDBSqlVisitor.java | 27 +- .../db/qp/strategy/LogicalGenerator.java | 8 +- .../optimizer/ConcatPathOptimizer.java | 8 +- .../dataset/groupby/GroupByLevelDataSet.java | 9 +- .../compaction/CompactionSchedulerTest.java | 2 +- .../iotdb/db/metadata/MetaUtilsTest.java | 4 +- .../iotdb/db/metadata/PartialPathTest.java | 158 ------ .../iotdb/db/metadata/SchemaBasicTest.java | 152 ++---- .../db/metadata/mtree/MTreeAboveSGTest.java | 23 +- .../mtree/schemafile/SchemaFileTest.java | 4 +- .../metadata/upgrade/MetadataUpgradeTest.java | 2 +- .../read/DeviceSchemaScanNodeSerdeTest.java | 2 +- .../read/SchemaCountNodeSerdeTest.java | 4 +- .../TimeSeriesSchemaScanNodeSerdeTest.java | 2 +- .../node/sink/FragmentSinkNodeSerdeTest.java | 7 +- .../physical/PhysicalPlanSerializeTest.java | 6 +- tsfile/pom.xml | 10 + .../common/constant/TsFileConstant.java | 7 + .../tsfile/exception/PathParseException.java | 26 + .../apache/iotdb/tsfile/read/common/Path.java | 78 +-- .../common/parser/PathNodesGenerator.java | 94 ++++ .../read/common/parser/PathParseError.java | 39 ++ .../read/common/parser/PathVisitor.java | 75 +++ .../iotdb/tsfile/read/common/PathTest.java | 163 +++++- 66 files changed, 1673 insertions(+), 715 deletions(-) create mode 100644 antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IdentifierParser.g4 create mode 100644 antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/PathParser.g4 delete mode 100644 node-commons/src/test/java/org/apache/iotdb/commons/utils/PathUtilsTest.java delete mode 100644 server/src/test/java/org/apache/iotdb/db/metadata/PartialPathTest.java create mode 100644 tsfile/src/main/java/org/apache/iotdb/tsfile/exception/PathParseException.java create mode 100644 tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/parser/PathNodesGenerator.java create mode 100644 tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/parser/PathParseError.java create mode 100644 tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/parser/PathVisitor.java diff --git a/antlr/pom.xml b/antlr/pom.xml index 64015796385e..a66513db4c64 100644 --- a/antlr/pom.xml +++ b/antlr/pom.xml @@ -47,6 +47,7 @@ false true + src/main/antlr4/org/apache/iotdb/db/qp/sql antlr4 diff --git a/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IdentifierParser.g4 b/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IdentifierParser.g4 new file mode 100644 index 000000000000..87814a428875 --- /dev/null +++ b/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IdentifierParser.g4 @@ -0,0 +1,179 @@ +/* + * 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. + */ + +parser grammar IdentifierParser; + +options { tokenVocab=SqlLexer; } + +identifier + : keyWords + | ID + | QUOTED_ID + ; + + +// List of keywords, new keywords should be added into this list. + +keyWords + : ADD + | AFTER + | ALIAS + | ALIGN + | ALIGNED + | ALL + | ALTER + | ANY + | APPEND + | AS + | ASC + | ATTRIBUTES + | AUTOREGISTER + | BEFORE + | BEGIN + | BOUNDARY + | BY + | CACHE + | CHILD + | CLEAR + | COMPRESSION + | COMPRESSOR + | CONCAT + | CONFIGURATION + | CONTINUOUS + | COUNT + | CONTAIN + | CQ + | CQS + | CREATE + | DATATYPE + | DEBUG + | DELETE + | DESC + | DESCRIBE + | DEVICE + | DEVICES + | DISABLE + | DROP + | ENCODING + | END + | EVERY + | EXPLAIN + | FILL + | FLUSH + | FOR + | FROM + | FULL + | FUNCTION + | FUNCTIONS + | GLOBAL + | GRANT + | GROUP + | INDEX + | INFO + | INSERT + | INTO + | KILL + | LABEL + | LAST + | LATEST + | LEVEL + | LIKE + | LIMIT + | LINEAR + | LINK + | LIST + | LOAD + | LOCK + | MERGE + | METADATA + | NODES + | NOW + | OF + | OFF + | OFFSET + | ON + | ORDER + | PARTITION + | PASSWORD + | PATHS + | PIPE + | PIPES + | PIPESERVER + | PIPESINK + | PIPESINKS + | PIPESINKTYPE + | PREVIOUS + | PREVIOUSUNTILLAST + | PRIVILEGES + | PROCESSLIST + | PROPERTY + | PRUNE + | QUERIES + | QUERY + | READONLY + | REGEXP + | REMOVE + | RENAME + | RESAMPLE + | RESOURCE + | REVOKE + | ROLE + | SCHEMA + | SELECT + | SET + | SETTLE + | SGLEVEL + | SHOW + | SLIMIT + | SOFFSET + | STORAGE + | START + | STOP + | SYSTEM + | TAGS + | TASK + | TEMPLATE + | TEMPLATES + | TIMESERIES + | TO + | TOLERANCE + | TOP + | TRACING + | TRIGGER + | TRIGGERS + | TTL + | UNLINK + | UNLOAD + | UNSET + | UPDATE + | UPSERT + | USER + | USING + | VALUES + | VERIFY + | VERSION + | WHERE + | WITH + | WITHOUT + | WRITABLE + | DATATYPE_VALUE + | ENCODING_VALUE + | COMPRESSOR_VALUE + | PRIVILEGE_VALUE + ; \ No newline at end of file diff --git a/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/InfluxDBSqlParser.g4 b/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/InfluxDBSqlParser.g4 index 0ffdcb9f2288..381f3e297977 100644 --- a/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/InfluxDBSqlParser.g4 +++ b/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/InfluxDBSqlParser.g4 @@ -21,6 +21,8 @@ parser grammar InfluxDBSqlParser; options { tokenVocab=SqlLexer; } +import IdentifierParser; + singleStatement : statement SEMI? EOF ; @@ -71,20 +73,12 @@ fromClause nodeName : STAR - | ID - | QUOTED_ID + | identifier | LAST | COUNT | DEVICE ; -// Identifier - -identifier - : ID - | QUOTED_ID - ; - // Constant & Literal @@ -126,4 +120,4 @@ realLiteral datetimeLiteral : DATETIME_LITERAL | NOW LR_BRACKET RR_BRACKET - ; + ; \ No newline at end of file diff --git a/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 b/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 index 56ca7bd9b576..d1b1565ceca8 100644 --- a/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 +++ b/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 @@ -21,6 +21,7 @@ parser grammar IoTDBSqlParser; options { tokenVocab=SqlLexer; } +import IdentifierParser; /** * 1. Top Level Description @@ -789,14 +790,6 @@ wildcard ; -// Identifier - -identifier - : ID - | QUOTED_ID - ; - - // Constant & Literal constant diff --git a/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/PathParser.g4 b/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/PathParser.g4 new file mode 100644 index 000000000000..546be58b6eee --- /dev/null +++ b/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/PathParser.g4 @@ -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. + */ + +parser grammar PathParser; + +options { tokenVocab=SqlLexer; } + +import IdentifierParser; + +/** + * PartialPath and Path used by Session API and TsFile API should be parsed by Antlr4. + */ + +path + : prefixPath EOF + | suffixPath EOF + ; + +prefixPath + : ROOT (DOT nodeName)* + ; + +suffixPath + : nodeName (DOT nodeName)* + ; + +nodeName + : wildcard + | wildcard? identifier wildcard? + | identifier + ; + +wildcard + : STAR + | DOUBLE_STAR + ; diff --git a/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/SqlLexer.g4 b/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/SqlLexer.g4 index f92fcbba5b4f..04bad754a26e 100644 --- a/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/SqlLexer.g4 +++ b/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/SqlLexer.g4 @@ -32,7 +32,7 @@ WS /** - * 2. Keywords + * 2. Keywords, new keywords should be added into IdentifierParser.g4 */ // Common Keywords @@ -975,7 +975,7 @@ NAN_LITERAL /** - * 6. Identifier + * 6. ID */ ID diff --git a/client-py/tests/test_dataframe.py b/client-py/tests/test_dataframe.py index f95ade6e0895..225556eed37d 100644 --- a/client-py/tests/test_dataframe.py +++ b/client-py/tests/test_dataframe.py @@ -29,7 +29,7 @@ def test_simple_query(): session.open(False) # Write data - session.insert_str_record("root.device", 123, "pressure", "15.0") + session.insert_str_record("root.device0", 123, "pressure", "15.0") # Read session_data_set = session.execute_query_statement("SELECT ** FROM root") @@ -37,7 +37,7 @@ def test_simple_query(): session.close() - assert list(df.columns) == ["Time", "root.device.pressure"] + assert list(df.columns) == ["Time", "root.device0.pressure"] assert_array_equal(df.values, [[123.0, 15.0]]) @@ -48,7 +48,7 @@ def test_non_time_query(): session.open(False) # Write data - session.insert_str_record("root.device", 123, "pressure", "15.0") + session.insert_str_record("root.device0", 123, "pressure", "15.0") # Read session_data_set = session.execute_query_statement("SHOW TIMESERIES") @@ -70,9 +70,9 @@ def test_non_time_query(): df.values, [ [ - "root.device.pressure", + "root.device0.pressure", None, - "root.device", + "root.device0", "FLOAT", "GORILLA", "SNAPPY", diff --git a/cluster/src/test/java/org/apache/iotdb/cluster/client/sync/SyncClientAdaptorTest.java b/cluster/src/test/java/org/apache/iotdb/cluster/client/sync/SyncClientAdaptorTest.java index e136a6bf607b..bc99e27b3d50 100644 --- a/cluster/src/test/java/org/apache/iotdb/cluster/client/sync/SyncClientAdaptorTest.java +++ b/cluster/src/test/java/org/apache/iotdb/cluster/client/sync/SyncClientAdaptorTest.java @@ -451,7 +451,7 @@ public Snapshot copy(Snapshot origin) { lastResult, SyncClientAdaptor.last( dataClient, - Collections.singletonList(new PartialPath("1")), + Collections.singletonList(new PartialPath("`1`")), Collections.singletonList(TSDataType.INT64.ordinal()), null, new QueryContext(), diff --git a/confignode/src/test/java/org/apache/iotdb/confignode/persistence/ClusterSchemaInfoTest.java b/confignode/src/test/java/org/apache/iotdb/confignode/persistence/ClusterSchemaInfoTest.java index 0735a83de099..a2eb84a6ec28 100644 --- a/confignode/src/test/java/org/apache/iotdb/confignode/persistence/ClusterSchemaInfoTest.java +++ b/confignode/src/test/java/org/apache/iotdb/confignode/persistence/ClusterSchemaInfoTest.java @@ -91,7 +91,7 @@ public void testSnapshot() throws IOException, IllegalPathException { storageGroupPathList.size(), clusterSchemaInfo.getStorageGroupNames().size()); GetStorageGroupReq getStorageGroupReq = - new GetStorageGroupReq(Arrays.asList(PathUtils.splitPathToDetachedPath("root.**"))); + new GetStorageGroupReq(Arrays.asList(PathUtils.splitPathToDetachedNodes("root.**"))); Map reloadResult = clusterSchemaInfo.getMatchedStorageGroupSchemas(getStorageGroupReq).getSchemaMap(); Assert.assertEquals(testMap, reloadResult); diff --git a/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBContinuousQueryIT.java b/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBContinuousQueryIT.java index 56ed6e6b2723..4dbb02d3e484 100644 --- a/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBContinuousQueryIT.java +++ b/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBContinuousQueryIT.java @@ -241,6 +241,48 @@ public void testContinuousQueryResultSeriesWithLevels() throws Exception { stopDataGenerator(); } + public void testContinuousQueryResultSeriesWithLevels1() throws Exception { + createTimeSeries( + new String[] { + "root.ln.wf01.wt01.ws01.`(temperature)`", + "root.ln.wf01.wt01.ws02.`(temperature)`", + "root.ln.wf01.wt02.ws01.`(temperature)`", + "root.ln.wf01.wt02.ws02.`(temperature)`", + "root.ln.wf02.wt01.ws01.`(temperature)`", + "root.ln.wf02.wt01.ws02.`(temperature)`", + "root.ln.wf02.wt02.ws01.`(temperature)`", + "root.ln.wf02.wt02.ws02.`(temperature)`" + }); + startDataGenerator(); + + Thread.sleep(500); + + statement.execute( + "CREATE CONTINUOUS QUERY cq1 " + + "BEGIN SELECT count(`(temperature)`) INTO temperature_cnt FROM root.ln.*.*.* " + + "GROUP BY time(1s), level=1,2 END"); + + Thread.sleep(5500); + + checkShowTimeSeriesResult( + new String[] { + "root.ln.wf01.wt01.ws01.`(temperature)`", + "root.ln.wf01.wt01.ws02.`(temperature)`", + "root.ln.wf01.wt02.ws01.`(temperature)`", + "root.ln.wf01.wt02.ws02.`(temperature)`", + "root.ln.wf02.wt01.ws01.`(temperature)`", + "root.ln.wf02.wt01.ws02.`(temperature)`", + "root.ln.wf02.wt02.ws01.`(temperature)`", + "root.ln.wf02.wt02.ws02.`(temperature)`", + "root.ln.wf01.temperature_cnt", + "root.ln.wf02.temperature_cnt" + }); + + statement.execute("DROP CONTINUOUS QUERY cq1"); + + stopDataGenerator(); + } + @Test public void testContinuousQueryResultSeriesWithDuplicatedTargetPaths() throws Exception { createTimeSeries( diff --git a/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBCreateTimeseriesIT.java b/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBCreateTimeseriesIT.java index aa759e873297..ad9d1e6fd0e4 100644 --- a/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBCreateTimeseriesIT.java +++ b/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBCreateTimeseriesIT.java @@ -212,7 +212,7 @@ public void testCreateTimeseriesWithSpecialCharacter() throws Exception { "root.sg.d.`a.b`", "root.sg.d.`a“(Φ)”b`", "root.sg.d.`a>b`", }; String[] timeSeriesResArray = { - "root.sg.d.`a.b`", "root.sg.d.a“(Φ)”b", "root.sg.d.a>b", + "root.sg.d.`a.b`", "root.sg.d.`a“(Φ)”b`", "root.sg.d.`a>b`", }; for (String timeSeries : timeSeriesArray) { diff --git a/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBQuotedPathIT.java b/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBQuotedPathIT.java index 8a1086dadade..716a3cd4a539 100644 --- a/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBQuotedPathIT.java +++ b/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBQuotedPathIT.java @@ -115,7 +115,7 @@ public void testIllegalStorageGroup() throws SQLException { statement.execute("SET STORAGE GROUP TO root.`\"ln`"); } catch (IoTDBSQLException e) { Assert.assertEquals( - "315: The storage group name can only be characters, numbers and underscores. root.\"ln is not a legal path", + "315: The storage group name can only be characters, numbers and underscores. root.`\"ln` is not a legal path", e.getMessage()); } catch (Exception e) { e.printStackTrace(); diff --git a/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBSelectIntoIT.java b/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBSelectIntoIT.java index df3541d9f577..70cfd8c50954 100644 --- a/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBSelectIntoIT.java +++ b/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBSelectIntoIT.java @@ -125,7 +125,7 @@ private static void createTimeSeries() throws MetadataException { null); IoTDB.schemaProcessor.createTimeseries( - new PartialPath("root.sg.d1.datatype"), + new PartialPath("root.sg.d1.`datatype`"), TSDataType.DOUBLE, TSEncoding.PLAIN, CompressionType.UNCOMPRESSED, diff --git a/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBSimpleQueryIT.java b/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBSimpleQueryIT.java index a7804ca2c9b5..aa42ea1591fd 100644 --- a/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBSimpleQueryIT.java +++ b/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBSimpleQueryIT.java @@ -1164,7 +1164,7 @@ public void testStorageGroupWithHyphenInName() { try (Connection connection = EnvFactory.getEnv().getConnection(); Statement statement = connection.createStatement()) { statement.setFetchSize(5); - statement.execute("SET STORAGE GROUP TO root.`group-with-hyphen`"); + statement.execute("SET STORAGE GROUP TO root.group_with_hyphen"); } catch (SQLException e) { fail(); } @@ -1180,7 +1180,7 @@ public void testStorageGroupWithHyphenInName() { for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) { builder.append(resultSet.getString(i)); } - Assert.assertEquals(builder.toString(), "root.group-with-hyphen"); + Assert.assertEquals(builder.toString(), "root.group_with_hyphen"); } } } diff --git a/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBSyntaxConventionIdentifierIT.java b/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBSyntaxConventionIdentifierIT.java index 81fb78512221..5a2243526953 100644 --- a/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBSyntaxConventionIdentifierIT.java +++ b/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBSyntaxConventionIdentifierIT.java @@ -47,6 +47,99 @@ public void tearDown() throws Exception { EnvFactory.getEnv().cleanAfterTest(); } + @Test + public void testKeyWord() { + String[] createNodeNames = { + "add", + "as", + "between", + "select", + "drop_trigger", + "REVOKE_USER_ROLE", + "pipesink", + "boolean", + "datatype", + "device", + }; + + String[] resultTimeseries = { + "root.sg1.d1.add", + "root.sg1.d1.as", + "root.sg1.d1.between", + "root.sg1.d1.select", + "root.sg1.d1.drop_trigger", + "root.sg1.d1.REVOKE_USER_ROLE", + "root.sg1.d1.pipesink", + "root.sg1.d1.boolean", + "root.sg1.d1.datatype", + "root.sg1.d1.device", + }; + + String[] selectNodeNames = { + "add", + "as", + "between", + "select", + "drop_trigger", + "REVOKE_USER_ROLE", + "pipesink", + "boolean", + "datatype", + "device", + }; + + String[] suffixInResultColumns = { + "add", + "as", + "between", + "select", + "drop_trigger", + "REVOKE_USER_ROLE", + "pipesink", + "boolean", + "datatype", + "device", + }; + + try (Connection connection = EnvFactory.getEnv().getConnection(); + Statement statement = connection.createStatement()) { + for (int i = 0; i < createNodeNames.length; i++) { + String createSql = + String.format("CREATE TIMESERIES root.sg1.d1.%s INT32", createNodeNames[i]); + String insertSql = + String.format("INSERT INTO root.sg1.d1(time, %s) VALUES(1, 1)", createNodeNames[i]); + statement.execute(createSql); + statement.execute(insertSql); + } + + boolean hasResult = statement.execute("SHOW TIMESERIES"); + Assert.assertTrue(hasResult); + Set expectedResult = new HashSet<>(Arrays.asList(resultTimeseries)); + + ResultSet resultSet = statement.getResultSet(); + while (resultSet.next()) { + Assert.assertTrue(expectedResult.contains(resultSet.getString("timeseries"))); + expectedResult.remove(resultSet.getString("timeseries")); + } + Assert.assertEquals(0, expectedResult.size()); + + for (int i = 0; i < selectNodeNames.length; i++) { + String selectSql = + String.format("SELECT %s FROM root.sg1.d1 WHERE time = 1", selectNodeNames[i]); + hasResult = statement.execute(selectSql); + Assert.assertTrue(hasResult); + + resultSet = statement.getResultSet(); + Assert.assertTrue(resultSet.next()); + Assert.assertEquals(1, resultSet.getInt("root.sg1.d1." + suffixInResultColumns[i])); + } + + } catch (SQLException e) { + e.printStackTrace(); + fail(); + } + } + @Test public void testNodeName() { String[] createNodeNames = { @@ -61,6 +154,8 @@ public void testNodeName() { "`a.'b`", "````", "`c.d.```", + "`abc`", + "a1_:@#${}" }; String[] resultTimeseries = { @@ -69,12 +164,14 @@ public void testNodeName() { "root.sg1.d1.aaa", "root.sg1.d1.select", "root.sg1.d1.`a.b`", - "root.sg1.d1.111", + "root.sg1.d1.`111`", "root.sg1.d1.`a``b`", "root.sg1.d1.`a.\"b`", "root.sg1.d1.`a.'b`", "root.sg1.d1.````", "root.sg1.d1.`c.d.```", + "root.sg1.d1.abc", + "root.sg1.d1.a1_:@#${}", }; String[] selectNodeNames = { @@ -89,6 +186,8 @@ public void testNodeName() { "`a.'b`", "````", "`c.d.```", + "abc", + "a1_:@#${}", }; String[] suffixInResultColumns = { @@ -97,12 +196,14 @@ public void testNodeName() { "aaa", "select", "`a.b`", - "111", + "`111`", "`a``b`", "`a.\"b`", "`a.'b`", "````", "`c.d.```", + "abc", + "a1_:@#${}", }; try (Connection connection = EnvFactory.getEnv().getConnection(); @@ -200,6 +301,56 @@ public void testNodeNameIllegal() { } catch (Exception ignored) { } + try { + statement.execute("create timeseries root.sg1.d1.``a` INT32"); + fail(); + } catch (Exception ignored) { + } + + // unsupported + try { + statement.execute("create timeseries root.sg1.d1.contains INT32"); + fail(); + } catch (Exception ignored) { + } + + try { + statement.execute("create timeseries root.sg1.d1.and INT32"); + fail(); + } catch (Exception ignored) { + } + + try { + statement.execute("create timeseries root.sg1.d1.or INT32"); + fail(); + } catch (Exception ignored) { + } + + try { + statement.execute("create timeseries root.sg1.d1.not INT32"); + fail(); + } catch (Exception ignored) { + } + + // reserved words can not be identifier + try { + statement.execute("create timeseries root.sg1.d1.root INT32"); + fail(); + } catch (Exception ignored) { + } + + try { + statement.execute("create timeseries root.sg1.d1.time INT32"); + fail(); + } catch (Exception ignored) { + } + + try { + statement.execute("create timeseries root.sg1.d1.timestamp INT32"); + fail(); + } catch (Exception ignored) { + } + } catch (SQLException e) { e.printStackTrace(); fail(); @@ -426,9 +577,8 @@ public void testTriggerNameIllegal() { Statement statement = connection.createStatement()) { try { statement.execute( - "create trigger trigger before insert on root.sg1.d1 " + "create trigger trigger` before insert on root.sg1.d1 " + "as 'org.apache.iotdb.db.engine.trigger.example.Accumulator'"); - fail(); } catch (Exception ignored) { } @@ -543,7 +693,7 @@ public void testTemplateName() { }; String[] resultNames = { - "id", "ID", "id0", "_id", "0id", "233", "ab!", "\"ab\"", "\"ac\"", "'ab'", "a.b", "a`b" + "id", "ID", "id0", "_id", "0id", "233", "ab!", "\"ab\"", "\\\"ac\\\"", "'ab'", "a.b", "a`b" }; try (Connection connection = EnvFactory.getEnv().getConnection(); diff --git a/integration/src/test/java/org/apache/iotdb/db/integration/withoutNullFilter/IoTDBWithoutNullAnyFilterIT.java b/integration/src/test/java/org/apache/iotdb/db/integration/withoutNullFilter/IoTDBWithoutNullAnyFilterIT.java index 076ed3e709f2..eb279eb206d3 100644 --- a/integration/src/test/java/org/apache/iotdb/db/integration/withoutNullFilter/IoTDBWithoutNullAnyFilterIT.java +++ b/integration/src/test/java/org/apache/iotdb/db/integration/withoutNullFilter/IoTDBWithoutNullAnyFilterIT.java @@ -1254,7 +1254,7 @@ public void withoutNullColumnsIsFullPathQueryTest() { Statement statement = connection.createStatement()) { boolean hasResultSet = statement.execute( - "select s2, s3 from root.test.** without null any(`root`.test.sg1.s2, `root`.test.sg2.s3)"); + "select s2, s3 from root.test.** without null any(root.test.sg1.s2, root.test.sg2.s3)"); String[] columns = new String[] { "root.test.sg1.s2", "root.test.sg1.s3", "root.test.sg2.s2", "root.test.sg2.s3" @@ -1282,7 +1282,7 @@ public void withoutNullColumnsIsFullPathQueryTest() { hasResultSet = statement.execute( - "select s2, s3 from root.test.sg1, root.test.sg2 without null any(`root`.test.sg1.s2)"); + "select s2, s3 from root.test.sg1, root.test.sg2 without null any(root.test.sg1.s2)"); Assert.assertTrue(hasResultSet); try (ResultSet resultSet = statement.getResultSet()) { @@ -1306,7 +1306,7 @@ public void withoutNullColumnsIsFullPathQueryTest() { hasResultSet = statement.execute( - "select s2, s3 from root.test.sg1, root.test.sg2 without null any(`root`.test.sg1.s2, s3)"); + "select s2, s3 from root.test.sg1, root.test.sg2 without null any(root.test.sg1.s2, s3)"); Assert.assertTrue(hasResultSet); try (ResultSet resultSet = statement.getResultSet()) { @@ -1330,7 +1330,7 @@ public void withoutNullColumnsIsFullPathQueryTest() { hasResultSet = statement.execute( - "select s2, s3 from root.test.sg1, root.test.sg2 without null any(`root`.test.*.s2)"); + "select s2, s3 from root.test.sg1, root.test.sg2 without null any(root.test.*.s2)"); Assert.assertTrue(hasResultSet); try (ResultSet resultSet = statement.getResultSet()) { @@ -1427,7 +1427,7 @@ public void withoutNullColumnsMisMatchSelectedQueryTest() { Statement statement = connection.createStatement()) { boolean hasResultSet = statement.execute( - "select last_value(*) from root.test.** group by([1,10), 2ms) without null any(last_value(`root.test.sg1.s2`)) align by device"); + "select last_value(*) from root.test.** group by([1,10), 2ms) without null any(last_value(root.test.sg1.s2)) align by device"); } catch (Exception e) { Assert.assertTrue(e.getMessage().contains(QueryPlan.WITHOUT_NULL_FILTER_ERROR_MESSAGE)); } diff --git a/integration/src/test/java/org/apache/iotdb/session/IoTDBSessionComplexIT.java b/integration/src/test/java/org/apache/iotdb/session/IoTDBSessionComplexIT.java index 29126d05f319..3aeb9de5eeba 100644 --- a/integration/src/test/java/org/apache/iotdb/session/IoTDBSessionComplexIT.java +++ b/integration/src/test/java/org/apache/iotdb/session/IoTDBSessionComplexIT.java @@ -419,17 +419,17 @@ public void test() session.createTimeseries( "root.sg1.d1.1_2", TSDataType.INT64, TSEncoding.RLE, CompressionType.SNAPPY); session.createTimeseries( - "root.sg1.d1.1+2+3", TSDataType.INT64, TSEncoding.RLE, CompressionType.SNAPPY); + "root.sg1.d1.`1+2+3`", TSDataType.INT64, TSEncoding.RLE, CompressionType.SNAPPY); session.createTimeseries( - "root.sg1.d1.1+2+4", TSDataType.INT64, TSEncoding.RLE, CompressionType.SNAPPY); + "root.sg1.d1.`1+2+4`", TSDataType.INT64, TSEncoding.RLE, CompressionType.SNAPPY); Assert.assertTrue(session.checkTimeseriesExists("root.sg1.d1.1_2")); Assert.assertTrue(session.checkTimeseriesExists("root.sg1.d1.`1+2+3`")); Assert.assertTrue(session.checkTimeseriesExists("root.sg1.d1.`1+2+4`")); - session.setStorageGroup("root.1"); + session.setStorageGroup("root.`1`"); session.createTimeseries( - "root.1.2.3", TSDataType.INT64, TSEncoding.RLE, CompressionType.SNAPPY); + "root.`1`.`2`.`3`", TSDataType.INT64, TSEncoding.RLE, CompressionType.SNAPPY); session.setStorageGroup("root.sg2"); session.createTimeseries( "root.sg2.d1.s1", TSDataType.INT64, TSEncoding.RLE, CompressionType.SNAPPY); diff --git a/integration/src/test/java/org/apache/iotdb/session/IoTDBSessionSimpleIT.java b/integration/src/test/java/org/apache/iotdb/session/IoTDBSessionSimpleIT.java index 98ec8ba4c71b..ae285a908cb6 100644 --- a/integration/src/test/java/org/apache/iotdb/session/IoTDBSessionSimpleIT.java +++ b/integration/src/test/java/org/apache/iotdb/session/IoTDBSessionSimpleIT.java @@ -530,7 +530,7 @@ public void createTimeSeriesWithDoubleTicks() session.setStorageGroup(storageGroup); session.createTimeseries( - "root.sg.\"my.device.with.colon:\".s", + "root.sg.`my.device.with.colon:`.s", TSDataType.INT64, TSEncoding.RLE, CompressionType.SNAPPY); @@ -561,6 +561,12 @@ public void createWrongTimeSeries() throws IoTDBConnectionException, StatementEx logger.error("", e); } + try { + session.createTimeseries("", TSDataType.INT64, TSEncoding.RLE, CompressionType.SNAPPY); + } catch (IoTDBConnectionException | StatementExecutionException e) { + logger.error("", e); + } + final SessionDataSet dataSet = session.executeQueryStatement("SHOW TIMESERIES"); assertFalse(dataSet.hasNext()); diff --git a/integration/src/test/java/org/apache/iotdb/session/IoTDBSessionSyntaxConventionIT.java b/integration/src/test/java/org/apache/iotdb/session/IoTDBSessionSyntaxConventionIT.java index e83726f5f210..37661b80ba2a 100644 --- a/integration/src/test/java/org/apache/iotdb/session/IoTDBSessionSyntaxConventionIT.java +++ b/integration/src/test/java/org/apache/iotdb/session/IoTDBSessionSyntaxConventionIT.java @@ -95,14 +95,14 @@ public void testInsert() throws Exception { String deviceId = "root.sg1.d1"; List measurements = new ArrayList<>(); - measurements.add("\"a“(Φ)”b\""); - measurements.add("\"a>b\""); - measurements.add("'a.b'"); - measurements.add("'a“(Φ)”b'"); - measurements.add("'a>b'"); - measurements.add("a“(Φ)”b"); - measurements.add("a>b"); - measurements.add("\\\"a"); + measurements.add("`\"a“(Φ)”b\"`"); + measurements.add("`\"a>b\"`"); + measurements.add("`'a.b'`"); + measurements.add("`'a“(Φ)”b'`"); + measurements.add("`'a>b'`"); + measurements.add("`a“(Φ)”b`"); + measurements.add("`a>b`"); + measurements.add("`\\\"a`"); List values = new ArrayList<>(); for (int i = 0; i < measurements.size(); i++) { @@ -118,7 +118,7 @@ public void testInsert() throws Exception { Assert.assertTrue(session.checkTimeseriesExists("root.sg1.d1.`'a>b'`")); Assert.assertTrue(session.checkTimeseriesExists("root.sg1.d1.`a“(Φ)”b`")); Assert.assertTrue(session.checkTimeseriesExists("root.sg1.d1.`a>b`")); - Assert.assertTrue(session.checkTimeseriesExists("root.sg1.d1.`\\\\\"a`")); + Assert.assertTrue(session.checkTimeseriesExists("root.sg1.d1.`\\\"a`")); session.close(); } diff --git a/integration/src/test/java/org/apache/iotdb/session/SessionTest.java b/integration/src/test/java/org/apache/iotdb/session/SessionTest.java index d92f5c82040a..3e33ebc662ea 100644 --- a/integration/src/test/java/org/apache/iotdb/session/SessionTest.java +++ b/integration/src/test/java/org/apache/iotdb/session/SessionTest.java @@ -612,40 +612,40 @@ public void testUnsetSchemaTemplate() // path does not exist test try { - session.unsetSchemaTemplate("root.sg.1", "template1"); + session.unsetSchemaTemplate("root.sg.`1`", "template1"); fail("No exception thrown."); } catch (Exception e) { - assertEquals("304: Path [root.sg.1] does not exist", e.getMessage()); + assertEquals("304: Path [root.sg.`1`] does not exist", e.getMessage()); } - session.setSchemaTemplate("template1", "root.sg.1"); + session.setSchemaTemplate("template1", "root.sg.`1`"); // template already exists test try { - session.setSchemaTemplate("template1", "root.sg.1"); + session.setSchemaTemplate("template1", "root.sg.`1`"); fail("No exception thrown."); } catch (Exception e) { - assertEquals("303: Template already exists on root.sg.1", e.getMessage()); + assertEquals("303: Template already exists on root.sg.`1`", e.getMessage()); } // template unset test - session.unsetSchemaTemplate("root.sg.1", "template1"); + session.unsetSchemaTemplate("root.sg.`1`", "template1"); - session.setSchemaTemplate("template1", "root.sg.1"); + session.setSchemaTemplate("template1", "root.sg.`1`"); // no template on path test - session.unsetSchemaTemplate("root.sg.1", "template1"); + session.unsetSchemaTemplate("root.sg.`1`", "template1"); try { - session.unsetSchemaTemplate("root.sg.1", "template1"); + session.unsetSchemaTemplate("root.sg.`1`", "template1"); fail("No exception thrown."); } catch (Exception e) { - assertEquals("324: NO template on root.sg.1", e.getMessage()); + assertEquals("324: NO template on root.sg.`1`", e.getMessage()); } // template is in use test - session.setSchemaTemplate("template1", "root.sg.1"); + session.setSchemaTemplate("template1", "root.sg.`1`"); - String deviceId = "root.sg.1.cd"; + String deviceId = "root.sg.`1`.cd"; List measurements = new ArrayList<>(); List types = new ArrayList<>(); measurements.add("s1"); @@ -664,10 +664,10 @@ public void testUnsetSchemaTemplate() } try { - session.unsetSchemaTemplate("root.sg.1", "template1"); + session.unsetSchemaTemplate("root.sg.`1`", "template1"); fail("No exception thrown."); } catch (Exception e) { - assertEquals("326: Template is in use on root.sg.1.cd", e.getMessage()); + assertEquals("326: Template is in use on root.sg.`1`.cd", e.getMessage()); } } } diff --git a/integration/src/test/java/org/apache/iotdb/session/template/TemplateUT.java b/integration/src/test/java/org/apache/iotdb/session/template/TemplateUT.java index efe2558d5ae8..6ea9dc5b9750 100644 --- a/integration/src/test/java/org/apache/iotdb/session/template/TemplateUT.java +++ b/integration/src/test/java/org/apache/iotdb/session/template/TemplateUT.java @@ -248,13 +248,13 @@ public void testUpdateTemplate() session.addAlignedMeasurementsInTemplate( "template1", - Collections.singletonList("append"), + Collections.singletonList("append1"), Collections.singletonList(TSDataType.INT64), Collections.singletonList(TSEncoding.RLE), Collections.singletonList(CompressionType.UNCOMPRESSED)); try { - session.deleteNodeInTemplate("template1", "append"); + session.deleteNodeInTemplate("template1", "append1"); fail(); } catch (StatementExecutionException e) { assertEquals( @@ -283,7 +283,7 @@ public void testUpdateTemplate() session.insertAlignedRecord( "root.sg.v1", 110L, - Collections.singletonList("append"), + Collections.singletonList("append1"), Collections.singletonList(TSDataType.TEXT), Collections.singletonList("aaa")); fail(); @@ -294,13 +294,13 @@ public void testUpdateTemplate() try { session.addAlignedMeasurementsInTemplate( "template1", - Collections.singletonList("append"), + Collections.singletonList("append1"), Collections.singletonList(TSDataType.TEXT), Collections.singletonList(TSEncoding.PLAIN), Collections.singletonList(CompressionType.UNCOMPRESSED)); fail(); } catch (StatementExecutionException e) { - assertEquals("315: Path duplicated: append is not a legal path", e.getMessage()); + assertEquals("315: Path duplicated: append1 is not a legal path", e.getMessage()); } try { @@ -347,7 +347,7 @@ public void testUpdateTemplate() session.insertAlignedRecord( "root.sg.v1", 110L, - Collections.singletonList("append"), + Collections.singletonList("append1"), Collections.singletonList(TSDataType.INT64), Collections.singletonList(12345L)); @@ -362,7 +362,7 @@ public void testUpdateTemplate() session.insertAlignedRecord( "root.sg.v1.d0", 110L, - Collections.singletonList("append"), + Collections.singletonList("append1"), Collections.singletonList(TSDataType.INT64), Collections.singletonList(12345L)); diff --git a/node-commons/pom.xml b/node-commons/pom.xml index 20247b3fe518..ef0ca1014631 100644 --- a/node-commons/pom.xml +++ b/node-commons/pom.xml @@ -83,6 +83,11 @@ iotdb-thrift-confignode ${project.version} + + org.apache.iotdb + iotdb-antlr + ${project.version} + io.jsonwebtoken diff --git a/node-commons/src/main/java/org/apache/iotdb/commons/path/PartialPath.java b/node-commons/src/main/java/org/apache/iotdb/commons/path/PartialPath.java index b11810e8e621..003e0e8f6b05 100644 --- a/node-commons/src/main/java/org/apache/iotdb/commons/path/PartialPath.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/path/PartialPath.java @@ -57,19 +57,25 @@ public PartialPath() {} /** * Construct the PartialPath using a String, will split the given String into String[] E.g., path - * = "root.sg.`d.1`.`s.1`" nodes = {"root", "sg", "d.1", "s.1"} + * = "root.sg.`d.1`.`s.1`" nodes = {"root", "sg", "`d.1`", "`s.1`"} * * @param path a full String of a time series path * @throws IllegalPathException */ public PartialPath(String path) throws IllegalPathException { - this.nodes = PathUtils.splitPathToDetachedPath(path); - this.fullPath = path; + this.nodes = PathUtils.splitPathToDetachedNodes(path); + // path is root.sg.`abc`, fullPath is root.sg.abc + // path is root.sg.`select`, fullPath is root.sg.select + // path is root.sg.`111`, fullPath is root.sg.`111` + // path is root.sg.`a.b`, fullPath is root.sg.`a.b` + // path is root.sg.`a``b`, fullPath is root.sg.`a``b` + this.fullPath = getFullPath(); } public PartialPath(String device, String measurement) throws IllegalPathException { - this.fullPath = device + TsFileConstant.PATH_SEPARATOR + measurement; - this.nodes = PathUtils.splitPathToDetachedPath(fullPath); + String path = device + TsFileConstant.PATH_SEPARATOR + measurement; + this.nodes = PathUtils.splitPathToDetachedNodes(path); + this.fullPath = getFullPath(); } /** @param partialNodes nodes of a time series path */ @@ -79,11 +85,16 @@ public PartialPath(String[] partialNodes) { /** * @param path path - * @param needSplit needSplit is basically false, whether need to be split to device and - * measurement, doesn't support escape character yet. + * @param needSplit whether to split path to nodes, needSplit can only be false. */ public PartialPath(String path, boolean needSplit) { - super(path, needSplit); + Validate.isTrue(!needSplit); + fullPath = path; + if ("".equals(path)) { + this.nodes = new String[] {}; + } else { + this.nodes = new String[] {path}; + } } /** @@ -336,9 +347,9 @@ public boolean overlapWith(PartialPath rPath) { @Override public String getFullPath() { if (fullPath == null) { - StringBuilder s = new StringBuilder(parseNodeString(nodes[0])); + StringBuilder s = new StringBuilder(nodes[0]); for (int i = 1; i < nodes.length; i++) { - s.append(TsFileConstant.PATH_SEPARATOR).append(parseNodeString(nodes[i])); + s.append(TsFileConstant.PATH_SEPARATOR).append(nodes[i]); } fullPath = s.toString(); } @@ -398,28 +409,14 @@ public String getDevice() { if (nodes.length == 1) { return ""; } - StringBuilder s = new StringBuilder(parseNodeString(nodes[0])); + StringBuilder s = new StringBuilder(nodes[0]); for (int i = 1; i < nodes.length - 1; i++) { s.append(TsFileConstant.PATH_SEPARATOR); - s.append(parseNodeString(nodes[i])); + s.append(nodes[i]); } device = s.toString(); - return device; - } - } - - /** - * wrap node that has . or ` in it with `` - * - * @param node - * @return - */ - protected String parseNodeString(String node) { - node = node.replace("`", "``"); - if (node.contains("`") || node.contains(".")) { - return "`" + node + "`"; } - return node; + return device; } // todo remove measurement related interface after invoker using MeasurementPath explicitly diff --git a/node-commons/src/main/java/org/apache/iotdb/commons/utils/PathUtils.java b/node-commons/src/main/java/org/apache/iotdb/commons/utils/PathUtils.java index ed541159b91b..941c7d25691e 100644 --- a/node-commons/src/main/java/org/apache/iotdb/commons/utils/PathUtils.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/utils/PathUtils.java @@ -19,12 +19,8 @@ package org.apache.iotdb.commons.utils; import org.apache.iotdb.commons.exception.IllegalPathException; -import org.apache.iotdb.tsfile.common.constant.TsFileConstant; - -import org.apache.commons.lang.StringEscapeUtils; - -import java.util.ArrayList; -import java.util.List; +import org.apache.iotdb.tsfile.exception.PathParseException; +import org.apache.iotdb.tsfile.read.common.parser.PathNodesGenerator; public class PathUtils { @@ -33,66 +29,22 @@ public class PathUtils { * @return string array. ex, [root, ln] * @throws IllegalPathException if path isn't correct, the exception will throw */ - public static String[] splitPathToDetachedPath(String path) throws IllegalPathException { - // NodeName is treated as identifier. When parsing identifier, unescapeJava is called. - // Therefore we call unescapeJava here. - path = StringEscapeUtils.unescapeJava(path); - if (path.endsWith(TsFileConstant.PATH_SEPARATOR)) { + public static String[] splitPathToDetachedNodes(String path) throws IllegalPathException { + if ("".equals(path)) { + return new String[] {}; + } + try { + return PathNodesGenerator.splitPathToNodes(path); + } catch (PathParseException e) { throw new IllegalPathException(path); } - List nodes = new ArrayList<>(); - int startIndex = 0; - int endIndex; - int length = path.length(); - for (int i = 0; i < length; i++) { - if (path.charAt(i) == TsFileConstant.PATH_SEPARATOR_CHAR) { - String node = path.substring(startIndex, i); - if (node.isEmpty()) { - throw new IllegalPathException(path); - } - nodes.add(node); - startIndex = i + 1; - } else if (path.charAt(i) == TsFileConstant.BACK_QUOTE) { - startIndex = i + 1; - endIndex = path.indexOf(TsFileConstant.BACK_QUOTE, startIndex); - if (endIndex == -1) { - // single '`', like root.sg.`s - throw new IllegalPathException(path); - } - while (endIndex != -1 && endIndex != length - 1) { - char afterQuote = path.charAt(endIndex + 1); - if (afterQuote == TsFileConstant.BACK_QUOTE) { - // for example, root.sg.``` - if (endIndex == length - 2) { - throw new IllegalPathException(path); - } - endIndex = path.indexOf(TsFileConstant.BACK_QUOTE, endIndex + 2); - } else if (afterQuote == '.') { - break; - } else { - throw new IllegalPathException(path); - } - } - // replace `` with ` in a quoted identifier - String node = path.substring(startIndex, endIndex).replace("``", "`"); - if (node.isEmpty()) { - throw new IllegalPathException(path); - } + } - nodes.add(node); - // skip the '.' after '`' - i = endIndex + 1; - startIndex = endIndex + 2; - } - } - // last node - if (startIndex <= path.length() - 1) { - String node = path.substring(startIndex); - if (node.isEmpty()) { - throw new IllegalPathException(path); - } - nodes.add(node); + public static void isLegalPath(String path) throws IllegalPathException { + try { + PathNodesGenerator.splitPathToNodes(path); + } catch (PathParseException e) { + throw new IllegalPathException(path); } - return nodes.toArray(new String[0]); } } diff --git a/node-commons/src/test/java/org/apache/iotdb/commons/path/PartialPathTest.java b/node-commons/src/test/java/org/apache/iotdb/commons/path/PartialPathTest.java index babcf9262c83..45d6ccd12fc5 100644 --- a/node-commons/src/test/java/org/apache/iotdb/commons/path/PartialPathTest.java +++ b/node-commons/src/test/java/org/apache/iotdb/commons/path/PartialPathTest.java @@ -23,8 +23,512 @@ import org.junit.Assert; import org.junit.Test; +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.fail; + public class PartialPathTest { + @Test + public void testLegalPath() throws IllegalPathException { + String[] nodes; + // empty path + PartialPath a = new PartialPath("", false); + Assert.assertEquals("", a.getFullPath()); + Assert.assertEquals(0, a.getNodes().length); + + // suffix path + PartialPath b = new PartialPath("s1"); + Assert.assertEquals("s1", b.getFullPath()); + Assert.assertEquals("s1", b.getNodes()[0]); + + // normal node + PartialPath c = new PartialPath("root.sg.a"); + Assert.assertEquals("root.sg.a", c.getFullPath()); + nodes = new String[] {"root", "sg", "a"}; + checkNodes(nodes, c.getNodes()); + + // quoted node + PartialPath d = new PartialPath("root.sg.`a.b`"); + Assert.assertEquals("root.sg.`a.b`", d.getFullPath()); + nodes = new String[] {"root", "sg", "`a.b`"}; + checkNodes(nodes, d.getNodes()); + + PartialPath e = new PartialPath("root.sg.`a.``b`"); + Assert.assertEquals("root.sg.`a.``b`", e.getFullPath()); + nodes = new String[] {"root", "sg", "`a.``b`"}; + checkNodes(nodes, e.getNodes()); + + PartialPath f = new PartialPath("root.`sg\"`.`a.``b`"); + Assert.assertEquals("root.`sg\"`.`a.``b`", f.getFullPath()); + nodes = new String[] {"root", "`sg\"`", "`a.``b`"}; + checkNodes(nodes, f.getNodes()); + + PartialPath g = new PartialPath("root.sg.`a.b\\\\`"); + Assert.assertEquals("root.sg.`a.b\\\\`", g.getFullPath()); + nodes = new String[] {"root", "sg", "`a.b\\\\`"}; + checkNodes(nodes, g.getNodes()); + + // quoted node of digits + PartialPath h = new PartialPath("root.sg.`111`"); + Assert.assertEquals("root.sg.`111`", h.getFullPath()); + nodes = new String[] {"root", "sg", "`111`"}; + checkNodes(nodes, h.getNodes()); + + // quoted node of key word + PartialPath i = new PartialPath("root.sg.`select`"); + Assert.assertEquals("root.sg.select", i.getFullPath()); + nodes = new String[] {"root", "sg", "select"}; + checkNodes(nodes, i.getNodes()); + + // wildcard + PartialPath j = new PartialPath("root.sg.`a*b`"); + Assert.assertEquals("root.sg.`a*b`", j.getFullPath()); + nodes = new String[] {"root", "sg", "`a*b`"}; + checkNodes(nodes, j.getNodes()); + + PartialPath k = new PartialPath("root.sg.*"); + Assert.assertEquals("root.sg.*", k.getFullPath()); + nodes = new String[] {"root", "sg", "*"}; + checkNodes(nodes, k.getNodes()); + + PartialPath l = new PartialPath("root.sg.**"); + Assert.assertEquals("root.sg.**", l.getFullPath()); + nodes = new String[] {"root", "sg", "**"}; + checkNodes(nodes, l.getNodes()); + + // raw key word + PartialPath m = new PartialPath("root.sg.select"); + Assert.assertEquals("root.sg.select", m.getFullPath()); + nodes = new String[] {"root", "sg", "select"}; + checkNodes(nodes, m.getNodes()); + + PartialPath n = new PartialPath("root.sg.device"); + Assert.assertEquals("root.sg.device", n.getFullPath()); + nodes = new String[] {"root", "sg", "device"}; + checkNodes(nodes, n.getNodes()); + + PartialPath o = new PartialPath("root.sg.datatype"); + Assert.assertEquals("root.sg.datatype", o.getFullPath()); + nodes = new String[] {"root", "sg", "datatype"}; + checkNodes(nodes, o.getNodes()); + + PartialPath r = new PartialPath("root.sg.boolean"); + Assert.assertEquals("root.sg.boolean", r.getFullPath()); + nodes = new String[] {"root", "sg", "boolean"}; + checkNodes(nodes, r.getNodes()); + + PartialPath s = new PartialPath("root.sg.DROP_TRIGGER"); + Assert.assertEquals("root.sg.DROP_TRIGGER", s.getFullPath()); + nodes = new String[] {"root", "sg", "DROP_TRIGGER"}; + checkNodes(nodes, s.getNodes()); + + PartialPath t = new PartialPath("root.sg.`abc`"); + Assert.assertEquals("root.sg.abc", t.getFullPath()); + nodes = new String[] {"root", "sg", "abc"}; + checkNodes(nodes, t.getNodes()); + } + + @Test + public void testIllegalPath() { + try { + new PartialPath("root.sg.d1.```"); + fail(); + } catch (IllegalPathException ignored) { + } + + try { + new PartialPath("root.sg.d1\na"); + fail(); + } catch (IllegalPathException ignored) { + } + + try { + new PartialPath("root.sg.`d1`..`aa``b`"); + fail(); + } catch (IllegalPathException ignored) { + } + + try { + new PartialPath("root.sg.d1.`s+`-1\"`"); + fail(); + } catch (IllegalPathException ignored) { + } + + try { + new PartialPath("root..a"); + fail(); + } catch (IllegalPathException ignored) { + } + + try { + new PartialPath("root.sg.d1."); + fail(); + } catch (IllegalPathException ignored) { + } + + try { + new PartialPath("root.sg.111"); + fail(); + } catch (IllegalPathException ignored) { + } + + try { + new PartialPath("root.sg.and"); + fail(); + } catch (IllegalPathException ignored) { + } + + try { + new PartialPath("root.sg.or"); + fail(); + } catch (IllegalPathException ignored) { + } + + try { + new PartialPath("root.sg.not"); + fail(); + } catch (IllegalPathException ignored) { + } + + try { + new PartialPath("root.sg.contains"); + fail(); + } catch (IllegalPathException ignored) { + } + + try { + new PartialPath("root.sg.watermark_embedding"); + fail(); + } catch (IllegalPathException ignored) { + } + + try { + new PartialPath("root.sg.time"); + fail(); + } catch (IllegalPathException ignored) { + } + + try { + new PartialPath("root.sg.root"); + fail(); + } catch (IllegalPathException ignored) { + } + + try { + new PartialPath("root.sg.timestamp"); + fail(); + } catch (IllegalPathException ignored) { + } + } + + @Test + public void testLegalDeviceAndMeasurement() throws IllegalPathException { + String[] nodes; + // normal node + PartialPath a = new PartialPath("root.sg", "s1"); + Assert.assertEquals("root.sg.s1", a.getFullPath()); + nodes = new String[] {"root", "sg", "s1"}; + checkNodes(nodes, a.getNodes()); + + PartialPath b = new PartialPath("root.sg", "s2"); + Assert.assertEquals("root.sg.s2", b.getFullPath()); + nodes = new String[] {"root", "sg", "s2"}; + checkNodes(nodes, b.getNodes()); + + PartialPath c = new PartialPath("root.sg", "a"); + Assert.assertEquals("root.sg.a", c.getFullPath()); + nodes = new String[] {"root", "sg", "a"}; + checkNodes(nodes, c.getNodes()); + + // quoted node + PartialPath d = new PartialPath("root.sg", "`a.b`"); + Assert.assertEquals("root.sg.`a.b`", d.getFullPath()); + nodes = new String[] {"root", "sg", "`a.b`"}; + checkNodes(nodes, d.getNodes()); + + PartialPath e = new PartialPath("root.sg", "`a.``b`"); + Assert.assertEquals("root.sg.`a.``b`", e.getFullPath()); + nodes = new String[] {"root", "sg", "`a.``b`"}; + checkNodes(nodes, e.getNodes()); + + PartialPath f = new PartialPath("root.`sg\"`", "`a.``b`"); + Assert.assertEquals("root.`sg\"`.`a.``b`", f.getFullPath()); + nodes = new String[] {"root", "`sg\"`", "`a.``b`"}; + checkNodes(nodes, f.getNodes()); + + PartialPath g = new PartialPath("root.sg", "`a.b\\\\`"); + Assert.assertEquals("root.sg.`a.b\\\\`", g.getFullPath()); + nodes = new String[] {"root", "sg", "`a.b\\\\`"}; + checkNodes(nodes, g.getNodes()); + + // quoted node of digits + PartialPath h = new PartialPath("root.sg", "`111`"); + Assert.assertEquals("root.sg.`111`", h.getFullPath()); + nodes = new String[] {"root", "sg", "`111`"}; + checkNodes(nodes, h.getNodes()); + + // quoted node of key word + PartialPath i = new PartialPath("root.sg", "`select`"); + Assert.assertEquals("root.sg.select", i.getFullPath()); + nodes = new String[] {"root", "sg", "select"}; + checkNodes(nodes, i.getNodes()); + + // wildcard + PartialPath j = new PartialPath("root.sg", "`a*b`"); + Assert.assertEquals("root.sg.`a*b`", j.getFullPath()); + nodes = new String[] {"root", "sg", "`a*b`"}; + checkNodes(nodes, j.getNodes()); + + PartialPath k = new PartialPath("root.sg", "*"); + Assert.assertEquals("root.sg.*", k.getFullPath()); + nodes = new String[] {"root", "sg", "*"}; + checkNodes(nodes, k.getNodes()); + + PartialPath l = new PartialPath("root.sg", "**"); + Assert.assertEquals("root.sg.**", l.getFullPath()); + nodes = new String[] {"root", "sg", "**"}; + checkNodes(nodes, l.getNodes()); + + // other + PartialPath m = new PartialPath("root.sg", "`to`.be.prefix.s"); + Assert.assertEquals("root.sg.to.be.prefix.s", m.getFullPath()); + nodes = new String[] {"root", "sg", "to", "be", "prefix", "s"}; + checkNodes(nodes, m.getNodes()); + + PartialPath n = new PartialPath("root.sg", "`abc`"); + Assert.assertEquals("root.sg.abc", n.getFullPath()); + nodes = new String[] {"root", "sg", "abc"}; + checkNodes(nodes, n.getNodes()); + } + + @Test + public void testIllegalDeviceAndMeasurement() { + try { + new PartialPath("root.sg.d1", "```"); + fail(); + } catch (IllegalPathException ignored) { + } + + try { + new PartialPath("root.sg.d1.```", "s1"); + fail(); + } catch (IllegalPathException ignored) { + } + + try { + new PartialPath("root.sg.`d1`..a", "`aa``b`"); + fail(); + } catch (IllegalPathException ignored) { + } + + try { + new PartialPath("root.sg.`d1`.a", "s..`aa``b`"); + fail(); + } catch (IllegalPathException ignored) { + } + + try { + new PartialPath("root.sg.d1", "`s+`-1\"`"); + fail(); + } catch (IllegalPathException ignored) { + } + + try { + new PartialPath("root.sg.d1.`s+`-1\"`", "s1"); + fail(); + } catch (IllegalPathException ignored) { + } + + try { + new PartialPath("root.sg", "111"); + fail(); + } catch (IllegalPathException ignored) { + } + + try { + new PartialPath("root.sg.111", "s1"); + fail(); + } catch (IllegalPathException ignored) { + } + + try { + new PartialPath("root.sg.select`", "a"); + fail(); + } catch (IllegalPathException ignored) { + } + + try { + new PartialPath("root.sg.d1", "device`"); + fail(); + } catch (IllegalPathException ignored) { + } + + try { + new PartialPath("root.sg.d1", "contains"); + fail(); + } catch (IllegalPathException ignored) { + } + + try { + new PartialPath("root.sg", "watermark_embedding"); + fail(); + } catch (IllegalPathException ignored) { + } + + try { + new PartialPath("root.sg", "and"); + fail(); + } catch (IllegalPathException ignored) { + } + + try { + new PartialPath("root.sg.d1", "or"); + fail(); + } catch (IllegalPathException ignored) { + } + + try { + new PartialPath("root.sg.d1", "not"); + fail(); + } catch (IllegalPathException ignored) { + } + + try { + new PartialPath("root.sg.d1", "root"); + fail(); + } catch (IllegalPathException ignored) { + } + + try { + new PartialPath("root.sg.d1", "time"); + fail(); + } catch (IllegalPathException ignored) { + } + + try { + new PartialPath("root.sg.d1", "timestamp"); + fail(); + } catch (IllegalPathException ignored) { + } + } + + @Test + public void testConcatPath() { + String[] arr1 = new String[2]; + arr1[0] = "root"; + arr1[1] = "sg1"; + PartialPath a = new PartialPath(arr1); + String[] arr2 = new String[2]; + arr2[0] = "d1"; + arr2[1] = "s1"; + PartialPath b = new PartialPath(arr2); + Assert.assertEquals("[root, sg1, d1, s1]", Arrays.toString(a.concatPath(b).getNodes())); + Assert.assertEquals("s1", b.getTailNode()); + Assert.assertEquals("root.sg1.d1", a.concatPath(b).getDevicePath().getFullPath()); + Assert.assertEquals("root.sg1", a.toString()); + } + + @Test + public void testConcatArray() throws IllegalPathException { + PartialPath a = new PartialPath("root", "sg1"); + String[] arr2 = new String[2]; + arr2[0] = "d1"; + arr2[1] = "s1"; + a.concatPath(arr2); + Assert.assertEquals("[root, sg1, d1, s1]", Arrays.toString(a.getNodes())); + } + + @Test + public void testConcatNode() { + String[] arr1 = new String[2]; + arr1[0] = "root"; + arr1[1] = "sg1"; + PartialPath a = new PartialPath(arr1); + PartialPath b = a.concatNode("d1"); + Assert.assertEquals("[root, sg1, d1]", Arrays.toString(b.getNodes())); + Assert.assertEquals("root.sg1.d1", b.getFullPath()); + Assert.assertTrue(b.startsWith(arr1)); + Assert.assertEquals("root", b.getFirstNode()); + } + + @Test + public void testAlterPrefixPath() throws IllegalPathException { + // Plain path. + PartialPath p = new PartialPath("root.a.b.c"); + List results = p.alterPrefixPath(new PartialPath("root.a.b")); + Assert.assertEquals(results.toString(), 1, results.size()); + Assert.assertEquals("root.a.b.c", results.get(0).getFullPath()); + + // Path with single level wildcard. + p = new PartialPath("root.*.b.c"); + results = p.alterPrefixPath(new PartialPath("root.a.b")); + Assert.assertEquals(results.toString(), 1, results.size()); + Assert.assertEquals("root.a.b.c", results.get(0).getFullPath()); + + // Path with multi level wildcard. + p = new PartialPath("root.**.b.c"); + results = p.alterPrefixPath(new PartialPath("root.a.b")); + Assert.assertEquals(results.toString(), 3, results.size()); + Assert.assertTrue(results.toString(), results.contains(new PartialPath("root.a.b.c"))); + Assert.assertTrue(results.toString(), results.contains(new PartialPath("root.a.b.b.c"))); + Assert.assertTrue(results.toString(), results.contains(new PartialPath("root.a.b.**.b.c"))); + + p = new PartialPath("root.**"); + results = p.alterPrefixPath(new PartialPath("root.a.b")); + Assert.assertEquals(results.toString(), 2, results.size()); + Assert.assertTrue(results.toString(), results.contains(new PartialPath("root.a.b"))); + Assert.assertTrue(results.toString(), results.contains(new PartialPath("root.a.b.**"))); + + p = new PartialPath("root.**.b.**"); + results = p.alterPrefixPath(new PartialPath("root.a.b.c")); + Assert.assertEquals(results.toString(), 2, results.size()); + Assert.assertTrue(results.toString(), results.contains(new PartialPath("root.a.b.c"))); + Assert.assertTrue(results.toString(), results.contains(new PartialPath("root.a.b.c.**"))); + + p = new PartialPath("root.**.b.**.b"); + results = p.alterPrefixPath(new PartialPath("root.b.b.b")); + Assert.assertEquals(results.toString(), 2, results.size()); + Assert.assertTrue(results.toString(), results.contains(new PartialPath("root.b.b.b.b"))); + Assert.assertTrue(results.toString(), results.contains(new PartialPath("root.b.b.b.**.b"))); + + // Path cannot be altered. + p = new PartialPath("root.b.c.**"); + results = p.alterPrefixPath(new PartialPath("root.a.b.c")); + Assert.assertEquals(results.toString(), 0, results.size()); + } + + @Test + public void testMatchPath() throws IllegalPathException { + PartialPath p1 = new PartialPath("root.sg1.d1.*"); + + Assert.assertTrue(p1.matchFullPath(new PartialPath("root.sg1.d1.s2"))); + Assert.assertFalse(p1.matchFullPath(new PartialPath("root.sg1.d1"))); + Assert.assertFalse(p1.matchFullPath(new PartialPath("root.sg2.d1.*"))); + Assert.assertFalse(p1.matchFullPath(new PartialPath("", false))); + + PartialPath path = new PartialPath("root.sg.d.s"); + String[] patterns = { + "root.**", "root.**.s", "root.sg.*.s", "root.*.*.*", "root.sg.d.s", "root.s*.d.s" + }; + for (String pattern : patterns) { + Assert.assertTrue(new PartialPath(pattern).matchFullPath(path)); + } + } + + @Test + public void testPartialPathAndStringList() { + List paths = + PartialPath.fromStringList(Arrays.asList("root.sg1.d1.s1", "root.sg1.d1.s2")); + Assert.assertEquals("root.sg1.d1.s1", paths.get(0).getFullPath()); + Assert.assertEquals("root.sg1.d1.s2", paths.get(1).getFullPath()); + + List stringPaths = PartialPath.toStringList(paths); + Assert.assertEquals("root.sg1.d1.s1", stringPaths.get(0)); + Assert.assertEquals("root.sg1.d1.s2", stringPaths.get(1)); + } + @Test public void testOverlapWith() throws IllegalPathException { PartialPath[][] pathPairs = @@ -43,4 +547,11 @@ public void testOverlapWith() throws IllegalPathException { Assert.assertEquals(results[i], pathPairs[i][0].overlapWith(pathPairs[i][1])); } } + + private void checkNodes(String[] expected, String[] actual) { + Assert.assertEquals(expected.length, actual.length); + for (int i = 0; i < expected.length; i++) { + Assert.assertEquals(expected[i], actual[i]); + } + } } diff --git a/node-commons/src/test/java/org/apache/iotdb/commons/utils/PathUtilsTest.java b/node-commons/src/test/java/org/apache/iotdb/commons/utils/PathUtilsTest.java deleted file mode 100644 index 1e73e04b992b..000000000000 --- a/node-commons/src/test/java/org/apache/iotdb/commons/utils/PathUtilsTest.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * 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.iotdb.commons.utils; - -import org.apache.iotdb.commons.exception.IllegalPathException; - -import org.junit.Assert; -import org.junit.Test; - -import java.util.Arrays; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.fail; - -public class PathUtilsTest { - - @Test - public void testSplitPathToNodes() throws IllegalPathException { - assertArrayEquals( - Arrays.asList("root", "sg", "d1", "s1").toArray(), - PathUtils.splitPathToDetachedPath("root.sg.d1.s1")); - - assertArrayEquals( - Arrays.asList("root", "sg", "d1", "s+1").toArray(), - PathUtils.splitPathToDetachedPath("root.sg.d1.`s+1`")); - - assertArrayEquals( - Arrays.asList("root", "\"s g\"", "d1", "\"s+1\"").toArray(), - PathUtils.splitPathToDetachedPath("root.`\"s g\"`.d1.`\"s+1\"`")); - - assertArrayEquals( - Arrays.asList("root", "1").toArray(), PathUtils.splitPathToDetachedPath("root.1")); - - assertArrayEquals( - Arrays.asList("root", "sg", "d1", "s", "1").toArray(), - PathUtils.splitPathToDetachedPath("root.sg.d1.s.1")); - - assertArrayEquals( - Arrays.asList("root", "sg", "d1", "`a.b`").toArray(), - PathUtils.splitPathToDetachedPath("root.sg.d1.```a.b```")); - - assertArrayEquals( - Arrays.asList("root", "sg", "d1", "`").toArray(), - PathUtils.splitPathToDetachedPath("root.sg.d1.````")); - - assertArrayEquals( - Arrays.asList("root", "sg", "d1", "`").toArray(), - PathUtils.splitPathToDetachedPath("`root`.`sg`.`d1`.````")); - - assertArrayEquals( - Arrays.asList("root", "sg", "d1", "`").toArray(), - PathUtils.splitPathToDetachedPath("`root`.sg.`d1`.````")); - - assertArrayEquals( - Arrays.asList("root", "sg", "d1.`").toArray(), - PathUtils.splitPathToDetachedPath("root.sg.`d1.```")); - - assertArrayEquals( - Arrays.asList("root", "sg", "\"d").toArray(), - PathUtils.splitPathToDetachedPath("root.sg.`\\\"d`")); - - assertArrayEquals( - Arrays.asList("root", "sg", "\td").toArray(), - PathUtils.splitPathToDetachedPath("root.sg.`\\td`")); - - assertArrayEquals( - Arrays.asList("root", "sg", "\\td").toArray(), - PathUtils.splitPathToDetachedPath("root.sg.`\\\\td`")); - - assertArrayEquals( - Arrays.asList("root", "laptop", "d1", "\"1.2.3\"").toArray(), - PathUtils.splitPathToDetachedPath("root.laptop.d1.`\\\"1.2.3\\\"`")); - - try { - PathUtils.splitPathToDetachedPath("root.sg.d1.```"); - fail(); - } catch (IllegalPathException e) { - Assert.assertEquals("root.sg.d1.``` is not a legal path", e.getMessage()); - } - - try { - PathUtils.splitPathToDetachedPath("root.sg.`d1`..`aa``b`"); - fail(); - } catch (IllegalPathException e) { - Assert.assertEquals("root.sg.`d1`..`aa``b` is not a legal path", e.getMessage()); - } - - try { - PathUtils.splitPathToDetachedPath("root.sg.d1.`s+`-1\"`"); - fail(); - } catch (IllegalPathException e) { - Assert.assertEquals("root.sg.d1.`s+`-1\"` is not a legal path", e.getMessage()); - } - - try { - PathUtils.splitPathToDetachedPath("root..a"); - fail(); - } catch (IllegalPathException e) { - Assert.assertEquals("root..a is not a legal path", e.getMessage()); - } - - try { - PathUtils.splitPathToDetachedPath("root.sg.d1."); - fail(); - } catch (IllegalPathException e) { - Assert.assertEquals("root.sg.d1. is not a legal path", e.getMessage()); - } - } -} diff --git a/schema-engine-rocksdb/src/main/java/org/apache/iotdb/db/metadata/schemaregion/rocksdb/RSchemaRegion.java b/schema-engine-rocksdb/src/main/java/org/apache/iotdb/db/metadata/schemaregion/rocksdb/RSchemaRegion.java index af87c4f45300..25156fad17d6 100644 --- a/schema-engine-rocksdb/src/main/java/org/apache/iotdb/db/metadata/schemaregion/rocksdb/RSchemaRegion.java +++ b/schema-engine-rocksdb/src/main/java/org/apache/iotdb/db/metadata/schemaregion/rocksdb/RSchemaRegion.java @@ -584,7 +584,7 @@ public Pair> deleteTimeseries(PartialPath pathPattern, bool RMeasurementMNode deletedNode; try { path = RSchemaUtils.getPathByInnerName(new String(key)); - String[] nodes = PathUtils.splitPathToDetachedPath(path); + String[] nodes = PathUtils.splitPathToDetachedNodes(path); deletedNode = new RMeasurementMNode(path, value, readWriteHandler); atomicInteger.incrementAndGet(); try (WriteBatch batch = new WriteBatch()) { diff --git a/schema-engine-rocksdb/src/main/java/org/apache/iotdb/db/metadata/schemaregion/rocksdb/RSchemaUtils.java b/schema-engine-rocksdb/src/main/java/org/apache/iotdb/db/metadata/schemaregion/rocksdb/RSchemaUtils.java index ee18ef613cbb..f38d3290775d 100644 --- a/schema-engine-rocksdb/src/main/java/org/apache/iotdb/db/metadata/schemaregion/rocksdb/RSchemaUtils.java +++ b/schema-engine-rocksdb/src/main/java/org/apache/iotdb/db/metadata/schemaregion/rocksdb/RSchemaUtils.java @@ -500,7 +500,7 @@ public static String[] newStringArray(String[] oldArray) throws IllegalPathExcep for (String str : oldArray) { stringBuilder.append(PATH_SEPARATOR).append(str); } - return PathUtils.splitPathToDetachedPath(stringBuilder.substring(1)); + return PathUtils.splitPathToDetachedNodes(stringBuilder.substring(1)); } public static String replaceWildcard(int num) { diff --git a/server/pom.xml b/server/pom.xml index 9c85715a4078..e38b8641b19f 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -43,11 +43,6 @@ service-rpc ${project.version} - - org.apache.iotdb - iotdb-antlr - ${project.version} - org.apache.iotdb iotdb-consensus diff --git a/server/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java b/server/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java index 1177b9940f3b..6e9f0ce346ff 100644 --- a/server/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java +++ b/server/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java @@ -64,7 +64,7 @@ public class IoTDBConfig { "org.apache.iotdb.db.conf.directories.strategy."; private static final String DEFAULT_MULTI_DIR_STRATEGY = "MaxDiskUsableSpaceFirstStrategy"; - private static final String STORAGE_GROUP_MATCHER = "([a-zA-Z0-9_.\\-\\u2E80-\\u9FFF]+)"; + private static final String STORAGE_GROUP_MATCHER = "([a-zA-Z0-9`_.\\-\\u2E80-\\u9FFF]+)"; public static final Pattern STORAGE_GROUP_PATTERN = Pattern.compile(STORAGE_GROUP_MATCHER); // e.g., a31+/$%#&[]{}3e4, "a.b", 'a.b' diff --git a/server/src/main/java/org/apache/iotdb/db/engine/cq/ContinuousQueryTask.java b/server/src/main/java/org/apache/iotdb/db/engine/cq/ContinuousQueryTask.java index 9fd8399e9ad9..6a3117595dbc 100644 --- a/server/src/main/java/org/apache/iotdb/db/engine/cq/ContinuousQueryTask.java +++ b/server/src/main/java/org/apache/iotdb/db/engine/cq/ContinuousQueryTask.java @@ -168,16 +168,17 @@ protected List generateTargetPaths(List rawPaths) throws Ille return targetPaths; } - protected String fillTargetPathTemplate(PartialPath rawPath) { - String[] nodes = rawPath.getNodes(); - int indexOfLeftBracket = nodes[0].indexOf("("); + protected String fillTargetPathTemplate(PartialPath rawPath) throws IllegalPathException { + String fullPath = rawPath.getFullPath(); + int indexOfLeftBracket = fullPath.indexOf("("); if (indexOfLeftBracket != -1) { - nodes[0] = nodes[0].substring(indexOfLeftBracket + 1); + fullPath = fullPath.substring(indexOfLeftBracket + 1); } - int indexOfRightBracket = nodes[nodes.length - 1].indexOf(")"); + int indexOfRightBracket = fullPath.lastIndexOf(")"); if (indexOfRightBracket != -1) { - nodes[nodes.length - 1] = nodes[nodes.length - 1].substring(0, indexOfRightBracket); + fullPath = fullPath.substring(0, indexOfRightBracket); } + String[] nodes = new PartialPath(fullPath).getNodes(); StringBuffer sb = new StringBuffer(); Matcher m = PATH_NODE_NAME_PATTERN.matcher(this.continuousQueryPlan.getTargetPath().getFullPath()); diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/idtable/IDTableHashmapImpl.java b/server/src/main/java/org/apache/iotdb/db/metadata/idtable/IDTableHashmapImpl.java index 149620e9e326..8b454cdc0160 100644 --- a/server/src/main/java/org/apache/iotdb/db/metadata/idtable/IDTableHashmapImpl.java +++ b/server/src/main/java/org/apache/iotdb/db/metadata/idtable/IDTableHashmapImpl.java @@ -87,6 +87,7 @@ public IDTableHashmapImpl(File storageGroupDir) { * @param plan create aligned timeseries plan * @throws MetadataException if the device is not aligned, throw it */ + @Override public synchronized void createAlignedTimeseries(CreateAlignedTimeSeriesPlan plan) throws MetadataException { DeviceEntry deviceEntry = getDeviceEntryWithAlignedCheck(plan.getPrefixPath().toString(), true); @@ -113,6 +114,7 @@ public synchronized void createAlignedTimeseries(CreateAlignedTimeSeriesPlan pla * @param plan create timeseries plan * @throws MetadataException if the device is aligned, throw it */ + @Override public synchronized void createTimeseries(CreateTimeSeriesPlan plan) throws MetadataException { DeviceEntry deviceEntry = getDeviceEntryWithAlignedCheck(plan.getPath().getDevice(), false); SchemaEntry schemaEntry = @@ -134,6 +136,7 @@ public synchronized void createTimeseries(CreateTimeSeriesPlan plan) throws Meta * @return reusable device id * @throws MetadataException if insert plan's aligned value is inconsistent with device */ + @Override public synchronized IDeviceID getSeriesSchemas(InsertPlan plan) throws MetadataException { PartialPath devicePath = plan.getDevicePath(); String[] measurementList = plan.getMeasurements(); @@ -188,7 +191,7 @@ public synchronized IDeviceID getSeriesSchemas(InsertPlan plan) throws MetadataE // set reusable device id plan.setDeviceID(deviceEntry.getDeviceID()); // change device path to device id string for insertion - plan.setDevicePath(new PartialPath(deviceEntry.getDeviceID().toStringID())); + plan.setDevicePath(new PartialPath(deviceEntry.getDeviceID().toStringID(), false)); return deviceEntry.getDeviceID(); } @@ -200,6 +203,7 @@ public synchronized IDeviceID getSeriesSchemas(InsertPlan plan) throws MetadataE * @param measurementMNode the timeseries measurement mnode * @throws MetadataException if the timeseries is not exits */ + @Override public synchronized void registerTrigger(PartialPath fullPath, IMeasurementMNode measurementMNode) throws MetadataException { boolean isAligned = measurementMNode.getParent().isAligned(); @@ -215,6 +219,7 @@ public synchronized void registerTrigger(PartialPath fullPath, IMeasurementMNode * @param measurementMNode the timeseries measurement mnode * @throws MetadataException if the timeseries is not exits */ + @Override public synchronized void deregisterTrigger( PartialPath fullPath, IMeasurementMNode measurementMNode) throws MetadataException { boolean isAligned = measurementMNode.getParent().isAligned(); @@ -229,6 +234,7 @@ public synchronized void deregisterTrigger( * @param timeseriesID timeseries ID of the timeseries * @throws MetadataException if the timeseries is not exits */ + @Override public synchronized TimeValuePair getLastCache(TimeseriesID timeseriesID) throws MetadataException { return getSchemaEntry(timeseriesID).getCachedLast(); @@ -243,6 +249,7 @@ public synchronized TimeValuePair getLastCache(TimeseriesID timeseriesID) * @param latestFlushedTime last flushed time * @throws MetadataException if the timeseries is not exits */ + @Override public synchronized void updateLastCache( TimeseriesID timeseriesID, TimeValuePair pair, @@ -452,11 +459,13 @@ private SchemaEntry getSchemaEntry(TimeseriesID timeseriesID) throws MetadataExc return schemaEntry; } + @Override @TestOnly public Map[] getIdTables() { return idTables; } + @Override @TestOnly public IDiskSchemaManager getIDiskSchemaManager() { return IDiskSchemaManager; diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/idtable/entry/SHA256DeviceID.java b/server/src/main/java/org/apache/iotdb/db/metadata/idtable/entry/SHA256DeviceID.java index b465cd32138a..1042aa698573 100644 --- a/server/src/main/java/org/apache/iotdb/db/metadata/idtable/entry/SHA256DeviceID.java +++ b/server/src/main/java/org/apache/iotdb/db/metadata/idtable/entry/SHA256DeviceID.java @@ -36,7 +36,7 @@ public class SHA256DeviceID implements IDeviceID { long l3; long l4; - private static final String SEPARATOR = "#"; + private static final String SEPARATOR = "_"; /** using lots of message digest for improving parallelism */ private static MessageDigest[] md; @@ -75,6 +75,9 @@ public SHA256DeviceID(String deviceID) { * @param deviceID a sha 256 string */ private void fromSHA256String(String deviceID) { + if (deviceID.startsWith("`") && deviceID.endsWith("`")) { + deviceID = deviceID.substring(1, deviceID.length() - 1); + } String[] part = deviceID.split(SEPARATOR); l1 = Long.parseLong(part[0]); l2 = Long.parseLong(part[1]); @@ -147,7 +150,7 @@ public String toString() { @Override public String toStringID() { - return l1 + SEPARATOR + l2 + SEPARATOR + l3 + SEPARATOR + l4; + return "`" + l1 + SEPARATOR + l2 + SEPARATOR + l3 + SEPARATOR + l4 + "`"; } @Override diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/MTreeBelowSGCachedImpl.java b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/MTreeBelowSGCachedImpl.java index 8f881062a9ba..8795aabbe603 100644 --- a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/MTreeBelowSGCachedImpl.java +++ b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/MTreeBelowSGCachedImpl.java @@ -1150,7 +1150,7 @@ private void checkTemplateOnSubtree(IMNode node) throws MetadataException { public void checkIsTemplateCompatibleWithChild(IMNode node, Template template) throws MetadataException { for (String measurementPath : template.getSchemaMap().keySet()) { - String directNodeName = PathUtils.splitPathToDetachedPath(measurementPath)[0]; + String directNodeName = PathUtils.splitPathToDetachedNodes(measurementPath)[0]; if (store.hasChild(node, directNodeName)) { throw new MetadataException( "Node name " @@ -1208,7 +1208,7 @@ public boolean isTemplateAppendable(Template tarTemplate, List appendMea // node Set overlapSet = new HashSet<>(); for (String path : appendMeasurements) { - overlapSet.add(PathUtils.splitPathToDetachedPath(path)[0]); + overlapSet.add(PathUtils.splitPathToDetachedNodes(path)[0]); } while (setNodes.size() != 0) { diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/MTreeBelowSGMemoryImpl.java b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/MTreeBelowSGMemoryImpl.java index 2b2a7ff4da68..b3dc1b18daab 100644 --- a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/MTreeBelowSGMemoryImpl.java +++ b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/MTreeBelowSGMemoryImpl.java @@ -1048,7 +1048,7 @@ private void checkTemplateOnSubtree(IMNode node) throws MetadataException { public void checkIsTemplateCompatibleWithChild(IMNode node, Template template) throws MetadataException { for (String measurementPath : template.getSchemaMap().keySet()) { - String directNodeName = PathUtils.splitPathToDetachedPath(measurementPath)[0]; + String directNodeName = PathUtils.splitPathToDetachedNodes(measurementPath)[0]; if (node.hasChild(directNodeName)) { throw new MetadataException( "Node name " @@ -1099,7 +1099,7 @@ public boolean isTemplateAppendable(Template tarTemplate, List appendMea // node Set overlapSet = new HashSet<>(); for (String path : appendMeasurements) { - overlapSet.add(PathUtils.splitPathToDetachedPath(path)[0]); + overlapSet.add(PathUtils.splitPathToDetachedNodes(path)[0]); } while (setNodes.size() != 0) { diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/store/disk/schemafile/SchemaFile.java b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/store/disk/schemafile/SchemaFile.java index 37415c472595..89a9e93d1c48 100644 --- a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/store/disk/schemafile/SchemaFile.java +++ b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/store/disk/schemafile/SchemaFile.java @@ -207,7 +207,7 @@ public static ISchemaFile loadSchemaFile(File file) throws IOException, Metadata @Override public IMNode init() throws MetadataException { IMNode resNode; - String[] sgPathNodes = PathUtils.splitPathToDetachedPath(storageGroupName); + String[] sgPathNodes = PathUtils.splitPathToDetachedNodes(storageGroupName); if (isEntity) { resNode = setNodeAddress( diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/path/AlignedPath.java b/server/src/main/java/org/apache/iotdb/db/metadata/path/AlignedPath.java index 615fed6a47a7..e420cf4dde89 100644 --- a/server/src/main/java/org/apache/iotdb/db/metadata/path/AlignedPath.java +++ b/server/src/main/java/org/apache/iotdb/db/metadata/path/AlignedPath.java @@ -22,6 +22,7 @@ import org.apache.iotdb.commons.exception.IllegalPathException; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.commons.path.PathType; +import org.apache.iotdb.commons.utils.PathUtils; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.file.metadata.enums.TSEncoding; import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils; @@ -58,6 +59,10 @@ public AlignedPath() {} public AlignedPath(String vectorPath, List subSensorsList) throws IllegalPathException { super(vectorPath); + // check whether subSensor is legal + for (String subSensor : subSensorsList) { + PathUtils.isLegalPath(subSensor); + } this.measurementList = subSensorsList; } @@ -65,6 +70,10 @@ public AlignedPath( String vectorPath, List measurementList, List schemaList) throws IllegalPathException { super(vectorPath); + // check whether measurement is legal + for (String measurement : measurementList) { + PathUtils.isLegalPath(measurement); + } this.measurementList = measurementList; this.schemaList = schemaList; } @@ -72,12 +81,14 @@ public AlignedPath( public AlignedPath(String vectorPath, String subSensor) throws IllegalPathException { super(vectorPath); measurementList = new ArrayList<>(); + PathUtils.isLegalPath(subSensor); measurementList.add(subSensor); } - public AlignedPath(PartialPath vectorPath, String subSensor) { + public AlignedPath(PartialPath vectorPath, String subSensor) throws IllegalPathException { super(vectorPath.getNodes()); measurementList = new ArrayList<>(); + PathUtils.isLegalPath(subSensor); measurementList.add(subSensor); } diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/template/Template.java b/server/src/main/java/org/apache/iotdb/db/metadata/template/Template.java index ba57fdf35795..68f01446bbbb 100644 --- a/server/src/main/java/org/apache/iotdb/db/metadata/template/Template.java +++ b/server/src/main/java/org/apache/iotdb/db/metadata/template/Template.java @@ -98,7 +98,7 @@ public Template(CreateTemplatePlan plan) throws IllegalPathException { // If sublist of measurements has only one item, // but it share prefix with other aligned sublist, it will be aligned too String[] thisMeasurement = - PathUtils.splitPathToDetachedPath(plan.getMeasurements().get(i).get(0)); + PathUtils.splitPathToDetachedNodes(plan.getMeasurements().get(i).get(0)); String thisPrefix = joinBySeparator(Arrays.copyOf(thisMeasurement, thisMeasurement.length - 1)); isAlign = @@ -184,7 +184,7 @@ private void constructTemplateTree(String[] alignedPaths, IMeasurementSchema[] s if (getPathNodeInTemplate(path) != null) { throw new IllegalPathException("Path duplicated: " + path); } - pathNodes = PathUtils.splitPathToDetachedPath(path); + pathNodes = PathUtils.splitPathToDetachedNodes(path); if (pathNodes.length == 1) { prefix = ""; @@ -233,7 +233,7 @@ private IMeasurementMNode constructTemplateTree(String path, IMeasurementSchema if (getPathNodeInTemplate(path) != null) { throw new IllegalPathException("Path duplicated: " + path); } - String[] pathNode = PathUtils.splitPathToDetachedPath(path); + String[] pathNode = PathUtils.splitPathToDetachedNodes(path); IMNode cur = constructEntityPath(path); synchronized (this) { @@ -358,7 +358,7 @@ public IMNode getPathNodeInTemplate(PartialPath path) { } public IMNode getPathNodeInTemplate(String path) throws IllegalPathException { - return getPathNodeInTemplate(PathUtils.splitPathToDetachedPath(path)); + return getPathNodeInTemplate(PathUtils.splitPathToDetachedNodes(path)); } private IMNode getPathNodeInTemplate(String[] pathNodes) { @@ -380,7 +380,7 @@ private IMNode getPathNodeInTemplate(String[] pathNodes) { } public boolean isPathExistInTemplate(String path) throws IllegalPathException { - String[] pathNodes = PathUtils.splitPathToDetachedPath(path); + String[] pathNodes = PathUtils.splitPathToDetachedNodes(path); if (!directNodes.containsKey(pathNodes[0])) { return false; } @@ -400,7 +400,7 @@ public boolean isDirectNodeInTemplate(String nodeName) { } public boolean isPathMeasurement(String path) throws MetadataException { - String[] pathNodes = PathUtils.splitPathToDetachedPath(path); + String[] pathNodes = PathUtils.splitPathToDetachedNodes(path); if (!directNodes.containsKey(pathNodes[0])) { throw new PathNotExistException(path); } @@ -476,7 +476,7 @@ private String getFullPathWithoutTemplateName(IMNode node) { * @return null if need to add direct node, will never return a measurement. */ private IMNode constructEntityPath(String path) throws IllegalPathException { - String[] pathNodes = PathUtils.splitPathToDetachedPath(path); + String[] pathNodes = PathUtils.splitPathToDetachedNodes(path); if (pathNodes.length == 1) { return null; } @@ -532,7 +532,7 @@ public void addAlignedMeasurements( // If prefix exists and not aligned, it will throw exception // Prefix equality will be checked in constructTemplateTree - pathNode = PathUtils.splitPathToDetachedPath(measurements[0]); + pathNode = PathUtils.splitPathToDetachedNodes(measurements[0]); prefix = joinBySeparator(Arrays.copyOf(pathNode, pathNode.length - 1)); IMNode targetNode = getPathNodeInTemplate(prefix); if ((targetNode != null && !targetNode.getAsEntityMNode().isAligned()) @@ -541,7 +541,7 @@ public void addAlignedMeasurements( } for (int i = 0; i <= measurements.length - 1; i++) { - pathNode = PathUtils.splitPathToDetachedPath(measurements[i]); + pathNode = PathUtils.splitPathToDetachedNodes(measurements[i]); leafNodes[i] = pathNode[pathNode.length - 1]; } schema = constructSchemas(leafNodes, dataTypes, encodings, compressors); @@ -564,7 +564,7 @@ public void addUnalignedMeasurements( } for (int i = 0; i <= measurements.length - 1; i++) { - pathNode = PathUtils.splitPathToDetachedPath(measurements[i]); + pathNode = PathUtils.splitPathToDetachedNodes(measurements[i]); // If prefix exists and aligned, it will throw exception prefix = joinBySeparator(Arrays.copyOf(pathNode, pathNode.length - 1)); diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java index 937c81a04f66..8f9db1267fe5 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java @@ -99,12 +99,14 @@ import org.apache.iotdb.db.query.expression.unary.NegationExpression; import org.apache.iotdb.db.query.expression.unary.RegularExpression; import org.apache.iotdb.tsfile.common.conf.TSFileDescriptor; +import org.apache.iotdb.tsfile.common.constant.TsFileConstant; import org.apache.iotdb.tsfile.file.metadata.enums.CompressionType; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.file.metadata.enums.TSEncoding; import org.apache.iotdb.tsfile.utils.Pair; import org.apache.commons.lang.StringEscapeUtils; +import org.apache.commons.lang3.StringUtils; import java.time.ZoneId; import java.util.ArrayList; @@ -174,7 +176,7 @@ public void parseAlignedMeasurements( CreateAlignedTimeSeriesStatement createAlignedTimeSeriesStatement) { for (int i = 0; i < ctx.nodeNameWithoutWildcard().size(); i++) { createAlignedTimeSeriesStatement.addMeasurement( - parseNodeName(ctx.nodeNameWithoutWildcard(i).getText())); + parseNodeNameWithoutWildCard(ctx.nodeNameWithoutWildcard(i))); parseAttributeClauses(ctx.attributeClauses(i), createAlignedTimeSeriesStatement); } } @@ -970,7 +972,7 @@ private boolean parseInsertColumnSpec( List measurementList = new ArrayList<>(); for (IoTDBSqlParser.NodeNameWithoutWildcardContext measurementName : ctx.nodeNameWithoutWildcard()) { - measurementList.add(parseNodeName(measurementName.getText())); + measurementList.add(parseNodeNameWithoutWildCard(measurementName)); } insertStatement.setMeasurementList(measurementList.toArray(new String[0])); return (ctx.TIME() == null && ctx.TIMESTAMP() == null); @@ -1038,7 +1040,7 @@ private PartialPath parseFullPath(IoTDBSqlParser.FullPathContext ctx) { } for (IoTDBSqlParser.NodeNameWithoutWildcardContext nodeNameWithoutStar : nodeNamesWithoutStar) { i++; - path[i] = parseNodeName(nodeNameWithoutStar.getText()); + path[i] = parseNodeNameWithoutWildCard(nodeNameWithoutStar); } return new PartialPath(path); } @@ -1095,11 +1097,24 @@ private PartialPath convertConstantToPath(String src) throws IllegalPathExceptio } private String parseNodeName(IoTDBSqlParser.NodeNameContext ctx) { - return parseIdentifier(ctx.getText()); + return parseNodeString(ctx.getText()); } private String parseNodeNameWithoutWildCard(IoTDBSqlParser.NodeNameWithoutWildcardContext ctx) { - return parseIdentifier(ctx.getText()); + return parseNodeString(ctx.getText()); + } + + private String parseNodeString(String nodeName) { + if (nodeName.startsWith(TsFileConstant.BACK_QUOTE_STRING) + && nodeName.endsWith(TsFileConstant.BACK_QUOTE_STRING)) { + String unWrapped = nodeName.substring(1, nodeName.length() - 1); + if (StringUtils.isNumeric(unWrapped) + || !TsFileConstant.NODE_NAME_PATTERN.matcher(unWrapped).matches()) { + return nodeName; + } + return unWrapped; + } + return nodeName; } // Literals ======================================================================== @@ -1170,17 +1185,10 @@ private String parseStringLiteralInInsertValue(String src) { } private String parseIdentifier(String src) { - if (2 <= src.length() && src.charAt(0) == '`' && src.charAt(src.length() - 1) == '`') { - String unescaped = StringEscapeUtils.unescapeJava(src.substring(1, src.length() - 1)); - // replace `` with ` - return unescaped.replace("``", "`"); - } - return src; - } - - private String parseNodeName(String src) { - if (2 <= src.length() && src.charAt(0) == '`' && src.charAt(src.length() - 1) == '`') { - return src.substring(1, src.length() - 1); + if (src.startsWith(TsFileConstant.BACK_QUOTE_STRING) + && src.endsWith(TsFileConstant.BACK_QUOTE_STRING)) { + return src.substring(1, src.length() - 1) + .replace(TsFileConstant.DOUBLE_BACK_QUOTE_STRING, TsFileConstant.BACK_QUOTE_STRING); } return src; } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/StatementGenerator.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/StatementGenerator.java index c5428151588e..e07e11acfcd6 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/StatementGenerator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/StatementGenerator.java @@ -99,7 +99,8 @@ public static Statement createStatement(TSRawDataQueryReq rawDataQueryReq, ZoneI PartialPath path = new PartialPath(pathStr); fromComponent.addPrefixPath(path); } - selectComponent.addResultColumn(new ResultColumn(new TimeSeriesOperand(new PartialPath("")))); + selectComponent.addResultColumn( + new ResultColumn(new TimeSeriesOperand(new PartialPath("", false)))); // set query filter GreaterEqualExpression leftPredicate = @@ -134,10 +135,11 @@ public static Statement createStatement(TSLastDataQueryReq lastDataQueryReq, Zon PartialPath path = new PartialPath(pathStr); fromComponent.addPrefixPath(path); } - selectComponent.addResultColumn(new ResultColumn(new TimeSeriesOperand(new PartialPath("")))); + selectComponent.addResultColumn( + new ResultColumn(new TimeSeriesOperand(new PartialPath("", false)))); // set query filter - PartialPath timePath = new PartialPath(TIME); + PartialPath timePath = new PartialPath(TIME, false); BasicFunctionFilter basicFunctionFilter = new BasicFunctionFilter( FilterConstant.FilterType.GREATERTHANOREQUALTO, diff --git a/server/src/main/java/org/apache/iotdb/db/qp/physical/sys/SetTemplatePlan.java b/server/src/main/java/org/apache/iotdb/db/qp/physical/sys/SetTemplatePlan.java index 6e1c6a4b346f..5ab66113e8e7 100644 --- a/server/src/main/java/org/apache/iotdb/db/qp/physical/sys/SetTemplatePlan.java +++ b/server/src/main/java/org/apache/iotdb/db/qp/physical/sys/SetTemplatePlan.java @@ -43,7 +43,7 @@ public SetTemplatePlan() { public SetTemplatePlan(String templateName, String prefixPath) throws IllegalPathException { super(OperatorType.SET_TEMPLATE); - String[] pathNodes = PathUtils.splitPathToDetachedPath(prefixPath); + String[] pathNodes = PathUtils.splitPathToDetachedNodes(prefixPath); for (String s : pathNodes) { if (s.equals(IoTDBConstant.ONE_LEVEL_PATH_WILDCARD) || s.equals(IoTDBConstant.MULTI_LEVEL_PATH_WILDCARD)) { diff --git a/server/src/main/java/org/apache/iotdb/db/qp/physical/sys/UnsetTemplatePlan.java b/server/src/main/java/org/apache/iotdb/db/qp/physical/sys/UnsetTemplatePlan.java index dc064b0204e6..f10560801ebf 100644 --- a/server/src/main/java/org/apache/iotdb/db/qp/physical/sys/UnsetTemplatePlan.java +++ b/server/src/main/java/org/apache/iotdb/db/qp/physical/sys/UnsetTemplatePlan.java @@ -45,7 +45,7 @@ public UnsetTemplatePlan() { public UnsetTemplatePlan(String prefixPath, String templateName) throws IllegalPathException { super(Operator.OperatorType.UNSET_TEMPLATE); - String[] pathNodes = PathUtils.splitPathToDetachedPath(prefixPath); + String[] pathNodes = PathUtils.splitPathToDetachedNodes(prefixPath); for (String s : pathNodes) { if (s.equals(IoTDBConstant.ONE_LEVEL_PATH_WILDCARD) || s.equals(IoTDBConstant.MULTI_LEVEL_PATH_WILDCARD)) { diff --git a/server/src/main/java/org/apache/iotdb/db/qp/sql/IoTDBSqlVisitor.java b/server/src/main/java/org/apache/iotdb/db/qp/sql/IoTDBSqlVisitor.java index f4481bfc0b27..da67a66f7ed4 100644 --- a/server/src/main/java/org/apache/iotdb/db/qp/sql/IoTDBSqlVisitor.java +++ b/server/src/main/java/org/apache/iotdb/db/qp/sql/IoTDBSqlVisitor.java @@ -161,6 +161,7 @@ import org.antlr.v4.runtime.tree.TerminalNode; import org.apache.commons.lang.StringEscapeUtils; +import org.apache.commons.lang3.StringUtils; import java.io.File; import java.time.ZoneId; @@ -2432,13 +2433,25 @@ private PartialPath convertConstantToPath(String src) throws IllegalPathExceptio // node name - /** function for parsing node name. */ private String parseNodeName(IoTDBSqlParser.NodeNameContext ctx) { - return parseIdentifier(ctx.getText()); + return parseNodeString(ctx.getText()); } private String parseNodeNameWithoutWildCard(IoTDBSqlParser.NodeNameWithoutWildcardContext ctx) { - return parseIdentifier(ctx.getText()); + return parseNodeString(ctx.getText()); + } + + private String parseNodeString(String nodeName) { + if (nodeName.startsWith(TsFileConstant.BACK_QUOTE_STRING) + && nodeName.endsWith(TsFileConstant.BACK_QUOTE_STRING)) { + String unWrapped = nodeName.substring(1, nodeName.length() - 1); + if (StringUtils.isNumeric(unWrapped) + || !TsFileConstant.NODE_NAME_PATTERN.matcher(unWrapped).matches()) { + return nodeName; + } + return unWrapped; + } + return nodeName; } // alias @@ -3015,10 +3028,10 @@ private String parseStringLiteralInInsertValue(String src) { } private String parseIdentifier(String src) { - if (2 <= src.length() && src.charAt(0) == '`' && src.charAt(src.length() - 1) == '`') { - String unescaped = StringEscapeUtils.unescapeJava(src.substring(1, src.length() - 1)); - // replace `` with ` - return unescaped.replace("``", "`"); + if (src.startsWith(TsFileConstant.BACK_QUOTE_STRING) + && src.endsWith(TsFileConstant.BACK_QUOTE_STRING)) { + return src.substring(1, src.length() - 1) + .replace(TsFileConstant.DOUBLE_BACK_QUOTE_STRING, TsFileConstant.BACK_QUOTE_STRING); } return src; } diff --git a/server/src/main/java/org/apache/iotdb/db/qp/strategy/LogicalGenerator.java b/server/src/main/java/org/apache/iotdb/db/qp/strategy/LogicalGenerator.java index 742aaf1dc84b..9e12f0655c6e 100644 --- a/server/src/main/java/org/apache/iotdb/db/qp/strategy/LogicalGenerator.java +++ b/server/src/main/java/org/apache/iotdb/db/qp/strategy/LogicalGenerator.java @@ -114,14 +114,14 @@ public static Operator generate(TSRawDataQueryReq rawDataQueryReq, ZoneId zoneId PartialPath path = new PartialPath(p); fromOp.addPrefixTablePath(path); } - selectOp.addResultColumn(new ResultColumn(new TimeSeriesOperand(new PartialPath("")))); + selectOp.addResultColumn(new ResultColumn(new TimeSeriesOperand(new PartialPath("", false)))); queryOp.setSelectComponent(selectOp); queryOp.setFromComponent(fromOp); // set time filter operator FilterOperator filterOp = new FilterOperator(FilterType.KW_AND); - PartialPath timePath = new PartialPath(TIME); + PartialPath timePath = new PartialPath(TIME, false); filterOp.setSinglePath(timePath); Set pathSet = new HashSet<>(); pathSet.add(timePath); @@ -151,7 +151,7 @@ public static Operator generate(TSLastDataQueryReq req, ZoneId zoneId) FromComponent fromOp = new FromComponent(); SelectComponent selectOp = new SelectComponent(zoneId); - selectOp.addResultColumn(new ResultColumn(new TimeSeriesOperand(new PartialPath("")))); + selectOp.addResultColumn(new ResultColumn(new TimeSeriesOperand(new PartialPath("", false)))); for (String p : req.getPaths()) { PartialPath path = new PartialPath(p); @@ -161,7 +161,7 @@ public static Operator generate(TSLastDataQueryReq req, ZoneId zoneId) queryOp.setSelectComponent(selectOp); queryOp.setFromComponent(fromOp); - PartialPath timePath = new PartialPath(TIME); + PartialPath timePath = new PartialPath(TIME, false); BasicFunctionOperator basicFunctionOperator = new BasicFunctionOperator( diff --git a/server/src/main/java/org/apache/iotdb/db/qp/strategy/optimizer/ConcatPathOptimizer.java b/server/src/main/java/org/apache/iotdb/db/qp/strategy/optimizer/ConcatPathOptimizer.java index c64397e1731a..46a37fe3e9d6 100644 --- a/server/src/main/java/org/apache/iotdb/db/qp/strategy/optimizer/ConcatPathOptimizer.java +++ b/server/src/main/java/org/apache/iotdb/db/qp/strategy/optimizer/ConcatPathOptimizer.java @@ -146,7 +146,7 @@ private void concatWithoutNullColumns( ((TimeSeriesOperand) expression) .setPath( new PartialPath( - PathUtils.splitPathToDetachedPath( + PathUtils.splitPathToDetachedNodes( ((TimeSeriesOperand) expression) .getPath() .getFirstNode()))); // split path To nodes @@ -242,11 +242,7 @@ private void removeWildcardsWithoutNullColumns(QueryOperator queryOperator) String groupedPath = groupByLevelController.getGroupedPath(expression.getExpressionString()); if (groupedPath != null) { - try { - resultExpressions.add(new TimeSeriesOperand(new PartialPath(groupedPath))); - } catch (IllegalPathException e) { - throw new LogicalOptimizeException(e.getMessage()); - } + resultExpressions.add(new TimeSeriesOperand(new PartialPath(groupedPath, false))); } else { resultExpressions.add(expression); } diff --git a/server/src/main/java/org/apache/iotdb/db/query/dataset/groupby/GroupByLevelDataSet.java b/server/src/main/java/org/apache/iotdb/db/query/dataset/groupby/GroupByLevelDataSet.java index 700ddc316b44..42fe464e5e02 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/dataset/groupby/GroupByLevelDataSet.java +++ b/server/src/main/java/org/apache/iotdb/db/query/dataset/groupby/GroupByLevelDataSet.java @@ -19,7 +19,6 @@ package org.apache.iotdb.db.query.dataset.groupby; -import org.apache.iotdb.commons.exception.IllegalPathException; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.qp.physical.crud.GroupByTimePlan; import org.apache.iotdb.db.query.aggregation.AggregateResult; @@ -70,12 +69,8 @@ public GroupByLevelDataSet(GroupByTimePlan plan, GroupByTimeEngineDataSet dataSe if (paths.isEmpty()) { for (Map.Entry entry : groupPathResultMap.entrySet()) { - try { - String alias = plan.getGroupByLevelController().getAlias(entry.getKey()); - this.paths.add(new PartialPath(alias != null ? alias : entry.getKey())); - } catch (IllegalPathException e) { - logger.error("Query result IllegalPathException occurred: {}.", entry.getKey()); - } + String alias = plan.getGroupByLevelController().getAlias(entry.getKey()); + this.paths.add(new PartialPath(alias != null ? alias : entry.getKey(), false)); this.dataTypes.add(entry.getValue().getResultDataType()); } } diff --git a/server/src/test/java/org/apache/iotdb/db/engine/compaction/CompactionSchedulerTest.java b/server/src/test/java/org/apache/iotdb/db/engine/compaction/CompactionSchedulerTest.java index 3be629e8b609..d8abaa888a80 100644 --- a/server/src/test/java/org/apache/iotdb/db/engine/compaction/CompactionSchedulerTest.java +++ b/server/src/test/java/org/apache/iotdb/db/engine/compaction/CompactionSchedulerTest.java @@ -58,7 +58,7 @@ public class CompactionSchedulerTest { private static final Logger logger = LoggerFactory.getLogger(CompactionSchedulerTest.class); - static final String COMPACTION_TEST_SG = "root.compactionSchedulerTest-"; + static final String COMPACTION_TEST_SG = "root.compactionSchedulerTest_"; private static final boolean oldEnableInnerSeqCompaction = IoTDBDescriptor.getInstance().getConfig().isEnableSeqSpaceCompaction(); private static final boolean oldEnableInnerUnseqCompaction = diff --git a/server/src/test/java/org/apache/iotdb/db/metadata/MetaUtilsTest.java b/server/src/test/java/org/apache/iotdb/db/metadata/MetaUtilsTest.java index 6b83212a5e3d..9d329af3e0bb 100644 --- a/server/src/test/java/org/apache/iotdb/db/metadata/MetaUtilsTest.java +++ b/server/src/test/java/org/apache/iotdb/db/metadata/MetaUtilsTest.java @@ -72,9 +72,9 @@ public void testGetMultiFullPaths() { public void testGroupAlignedPath() throws MetadataException { List pathList = new ArrayList<>(); - MeasurementPath path1 = new MeasurementPath(new PartialPath("root.sg.device.s1"), null); + MeasurementPath path1 = new MeasurementPath(new PartialPath("root.sg.device1.s1"), null); pathList.add(path1); - MeasurementPath path2 = new MeasurementPath(new PartialPath("root.sg.device.s2"), null); + MeasurementPath path2 = new MeasurementPath(new PartialPath("root.sg.device1.s2"), null); pathList.add(path2); MeasurementPath path3 = new MeasurementPath(new PartialPath("root.sg.aligned_device.s1"), null); diff --git a/server/src/test/java/org/apache/iotdb/db/metadata/PartialPathTest.java b/server/src/test/java/org/apache/iotdb/db/metadata/PartialPathTest.java deleted file mode 100644 index dafdfbc7e591..000000000000 --- a/server/src/test/java/org/apache/iotdb/db/metadata/PartialPathTest.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * 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.iotdb.db.metadata; - -import org.apache.iotdb.commons.exception.IllegalPathException; -import org.apache.iotdb.commons.path.PartialPath; -import org.apache.iotdb.db.utils.EnvironmentUtils; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import java.util.Arrays; -import java.util.List; - -public class PartialPathTest { - @Before - public void setUp() { - EnvironmentUtils.envSetUp(); - } - - @After - public void tearDown() throws Exception { - EnvironmentUtils.cleanEnv(); - } - - @Test - public void testConcatPath() { - String[] arr1 = new String[2]; - arr1[0] = "root"; - arr1[1] = "sg1"; - PartialPath a = new PartialPath(arr1); - String[] arr2 = new String[2]; - arr2[0] = "d1"; - arr2[1] = "s1"; - PartialPath b = new PartialPath(arr2); - Assert.assertEquals("[root, sg1, d1, s1]", Arrays.toString(a.concatPath(b).getNodes())); - Assert.assertEquals("s1", b.getTailNode()); - Assert.assertEquals("root.sg1.d1", a.concatPath(b).getDevicePath().getFullPath()); - Assert.assertEquals("root.sg1", a.toString()); - } - - @Test - public void testConcatArray() throws IllegalPathException { - PartialPath a = new PartialPath("root", "sg1"); - String[] arr2 = new String[2]; - arr2[0] = "d1"; - arr2[1] = "s1"; - a.concatPath(arr2); - Assert.assertEquals("[root, sg1, d1, s1]", Arrays.toString(a.getNodes())); - } - - @Test - public void testConcatNode() { - String[] arr1 = new String[2]; - arr1[0] = "root"; - arr1[1] = "sg1"; - PartialPath a = new PartialPath(arr1); - PartialPath b = a.concatNode("d1"); - Assert.assertEquals("[root, sg1, d1]", Arrays.toString(b.getNodes())); - Assert.assertEquals("root.sg1.d1", b.getFullPath()); - Assert.assertTrue(b.startsWith(arr1)); - Assert.assertEquals("root", b.getFirstNode()); - } - - @Test - public void testAlterPrefixPath() throws IllegalPathException { - // Plain path. - PartialPath p = new PartialPath("root.a.b.c"); - List results = p.alterPrefixPath(new PartialPath("root.a.b")); - Assert.assertEquals(results.toString(), 1, results.size()); - Assert.assertEquals("root.a.b.c", results.get(0).getFullPath()); - - // Path with single level wildcard. - p = new PartialPath("root.*.b.c"); - results = p.alterPrefixPath(new PartialPath("root.a.b")); - Assert.assertEquals(results.toString(), 1, results.size()); - Assert.assertEquals("root.a.b.c", results.get(0).getFullPath()); - - // Path with multi level wildcard. - p = new PartialPath("root.**.b.c"); - results = p.alterPrefixPath(new PartialPath("root.a.b")); - Assert.assertEquals(results.toString(), 3, results.size()); - Assert.assertTrue(results.toString(), results.contains(new PartialPath("root.a.b.c"))); - Assert.assertTrue(results.toString(), results.contains(new PartialPath("root.a.b.b.c"))); - Assert.assertTrue(results.toString(), results.contains(new PartialPath("root.a.b.**.b.c"))); - - p = new PartialPath("root.**"); - results = p.alterPrefixPath(new PartialPath("root.a.b")); - Assert.assertEquals(results.toString(), 2, results.size()); - Assert.assertTrue(results.toString(), results.contains(new PartialPath("root.a.b"))); - Assert.assertTrue(results.toString(), results.contains(new PartialPath("root.a.b.**"))); - - p = new PartialPath("root.**.b.**"); - results = p.alterPrefixPath(new PartialPath("root.a.b.c")); - Assert.assertEquals(results.toString(), 2, results.size()); - Assert.assertTrue(results.toString(), results.contains(new PartialPath("root.a.b.c"))); - Assert.assertTrue(results.toString(), results.contains(new PartialPath("root.a.b.c.**"))); - - p = new PartialPath("root.**.b.**.b"); - results = p.alterPrefixPath(new PartialPath("root.b.b.b")); - Assert.assertEquals(results.toString(), 2, results.size()); - Assert.assertTrue(results.toString(), results.contains(new PartialPath("root.b.b.b.b"))); - Assert.assertTrue(results.toString(), results.contains(new PartialPath("root.b.b.b.**.b"))); - - // Path cannot be altered. - p = new PartialPath("root.b.c.**"); - results = p.alterPrefixPath(new PartialPath("root.a.b.c")); - Assert.assertEquals(results.toString(), 0, results.size()); - } - - @Test - public void testMatchPath() throws IllegalPathException { - PartialPath p1 = new PartialPath("root.sg1.d1.*"); - - Assert.assertTrue(p1.matchFullPath(new PartialPath("root.sg1.d1.s2"))); - Assert.assertFalse(p1.matchFullPath(new PartialPath("root.sg1.d1"))); - Assert.assertFalse(p1.matchFullPath(new PartialPath("root.sg2.d1.*"))); - Assert.assertFalse(p1.matchFullPath(new PartialPath(""))); - - PartialPath path = new PartialPath("root.sg.d.s"); - String[] patterns = { - "root.**", "root.**.s", "root.sg.*.s", "root.*.*.*", "root.sg.d.s", "root.s*.d.s" - }; - for (String pattern : patterns) { - Assert.assertTrue(new PartialPath(pattern).matchFullPath(path)); - } - } - - @Test - public void testPartialPathAndStringList() { - List paths = - PartialPath.fromStringList(Arrays.asList("root.sg1.d1.s1", "root.sg1.d1.s2")); - Assert.assertEquals("root.sg1.d1.s1", paths.get(0).getFullPath()); - Assert.assertEquals("root.sg1.d1.s2", paths.get(1).getFullPath()); - - List stringPaths = PartialPath.toStringList(paths); - Assert.assertEquals("root.sg1.d1.s1", stringPaths.get(0)); - Assert.assertEquals("root.sg1.d1.s2", stringPaths.get(1)); - } -} diff --git a/server/src/test/java/org/apache/iotdb/db/metadata/SchemaBasicTest.java b/server/src/test/java/org/apache/iotdb/db/metadata/SchemaBasicTest.java index 2a6b2232aebc..3e56b1ba4ab0 100644 --- a/server/src/test/java/org/apache/iotdb/db/metadata/SchemaBasicTest.java +++ b/server/src/test/java/org/apache/iotdb/db/metadata/SchemaBasicTest.java @@ -108,13 +108,13 @@ public void testAddPathAndExist() throws IllegalPathException { try { schemaProcessor.setStorageGroup(new PartialPath("root.laptop.d1")); - schemaProcessor.setStorageGroup(new PartialPath("root.1")); + schemaProcessor.setStorageGroup(new PartialPath("root.`1`")); } catch (MetadataException e) { e.printStackTrace(); fail(e.getMessage()); } - assertTrue(schemaProcessor.isPathExist(new PartialPath("root.1"))); + assertTrue(schemaProcessor.isPathExist(new PartialPath("root.`1`"))); try { schemaProcessor.setStorageGroup(new PartialPath("root.laptop")); @@ -158,7 +158,7 @@ public void testAddPathAndExist() throws IllegalPathException { TSFileDescriptor.getInstance().getConfig().getCompressor(), Collections.emptyMap()); schemaProcessor.createTimeseries( - new PartialPath("root.1.2.3"), + new PartialPath("root.`1`.`2`.`3`"), TSDataType.INT32, TSEncoding.RLE, TSFileDescriptor.getInstance().getConfig().getCompressor(), @@ -167,8 +167,8 @@ public void testAddPathAndExist() throws IllegalPathException { assertTrue(schemaProcessor.isPathExist(new PartialPath("root.laptop.d1.s1"))); assertTrue(schemaProcessor.isPathExist(new PartialPath("root.laptop.d1.1_2"))); assertTrue(schemaProcessor.isPathExist(new PartialPath("root.laptop.d1.`\"1.2.3\"`"))); - assertTrue(schemaProcessor.isPathExist(new PartialPath("root.1.2"))); - assertTrue(schemaProcessor.isPathExist(new PartialPath("root.1.2.3"))); + assertTrue(schemaProcessor.isPathExist(new PartialPath("root.`1`.`2`"))); + assertTrue(schemaProcessor.isPathExist(new PartialPath("root.`1`.`2`.`3`"))); } catch (MetadataException e1) { e1.printStackTrace(); fail(e1.getMessage()); @@ -246,31 +246,31 @@ public void testAddPathAndExist() throws IllegalPathException { try { schemaProcessor.deleteTimeseries(new PartialPath("root.laptop.d1.1_2")); schemaProcessor.deleteTimeseries(new PartialPath("root.laptop.d1.`\"1.2.3\"`")); - schemaProcessor.deleteTimeseries(new PartialPath("root.1.2.3")); + schemaProcessor.deleteTimeseries(new PartialPath("root.`1`.`2`.`3`")); } catch (MetadataException e) { e.printStackTrace(); fail(e.getMessage()); } assertFalse(schemaProcessor.isPathExist(new PartialPath("root.laptop.d1.1_2"))); assertFalse(schemaProcessor.isPathExist(new PartialPath("root.laptop.d1.`\"1.2.3\"`"))); - assertFalse(schemaProcessor.isPathExist(new PartialPath("root.1.2.3"))); - assertFalse(schemaProcessor.isPathExist(new PartialPath("root.1.2"))); - assertTrue(schemaProcessor.isPathExist(new PartialPath("root.1"))); + assertFalse(schemaProcessor.isPathExist(new PartialPath("root.`1`.`2`.`3`"))); + assertFalse(schemaProcessor.isPathExist(new PartialPath("root.`1`.`2`"))); + assertTrue(schemaProcessor.isPathExist(new PartialPath("root.`1`"))); try { - schemaProcessor.deleteStorageGroups(Collections.singletonList(new PartialPath("root.1"))); + schemaProcessor.deleteStorageGroups(Collections.singletonList(new PartialPath("root.`1`"))); } catch (MetadataException e) { e.printStackTrace(); fail(e.getMessage()); } - assertFalse(schemaProcessor.isPathExist(new PartialPath("root.1"))); + assertFalse(schemaProcessor.isPathExist(new PartialPath("root.`1`"))); - assertFalse(schemaProcessor.isPathExist(new PartialPath("root.template"))); - assertFalse(schemaProcessor.isPathExist(new PartialPath("root.template.d1"))); + assertFalse(schemaProcessor.isPathExist(new PartialPath("root.template0"))); + assertFalse(schemaProcessor.isPathExist(new PartialPath("root.template0.d1"))); try { schemaProcessor.createTimeseries( - new PartialPath("root.template.d2"), + new PartialPath("root.template0.d2"), TSDataType.INT32, TSEncoding.RLE, TSFileDescriptor.getInstance().getConfig().getCompressor(), @@ -282,19 +282,19 @@ public void testAddPathAndExist() throws IllegalPathException { try { schemaProcessor.createSchemaTemplate(getCreateTemplatePlan()); - schemaProcessor.setSchemaTemplate(new SetTemplatePlan("template1", "root.template")); + schemaProcessor.setSchemaTemplate(new SetTemplatePlan("template1", "root.template0")); schemaProcessor.setUsingSchemaTemplate( - new ActivateTemplatePlan(new PartialPath("root.template.d1"))); + new ActivateTemplatePlan(new PartialPath("root.template0.d1"))); } catch (MetadataException e) { e.printStackTrace(); fail(e.getMessage()); } - assertTrue(schemaProcessor.isPathExist(new PartialPath("root.template.d1"))); - assertTrue(schemaProcessor.isPathExist(new PartialPath("root.template.d1.s11"))); - assertFalse(schemaProcessor.isPathExist(new PartialPath("root.template.d2.s11"))); - assertTrue(schemaProcessor.isPathExist(new PartialPath("root.template.d1.vector"))); - assertTrue(schemaProcessor.isPathExist(new PartialPath("root.template.d1.vector.s0"))); + assertTrue(schemaProcessor.isPathExist(new PartialPath("root.template0.d1"))); + assertTrue(schemaProcessor.isPathExist(new PartialPath("root.template0.d1.s11"))); + assertFalse(schemaProcessor.isPathExist(new PartialPath("root.template0.d2.s11"))); + assertTrue(schemaProcessor.isPathExist(new PartialPath("root.template0.d1.vector"))); + assertTrue(schemaProcessor.isPathExist(new PartialPath("root.template0.d1.vector.s0"))); } /** @@ -607,11 +607,11 @@ public void testCheckStorageExistOfPath() { schemaProcessor.getBelongedStorageGroups(new PartialPath("root.vehicle")).isEmpty()); assertTrue( schemaProcessor - .getBelongedStorageGroups(new PartialPath("root.vehicle.device")) + .getBelongedStorageGroups(new PartialPath("root.vehicle.device0")) .isEmpty()); assertTrue( schemaProcessor - .getBelongedStorageGroups(new PartialPath("root.vehicle.device.sensor")) + .getBelongedStorageGroups(new PartialPath("root.vehicle.device0.sensor")) .isEmpty()); schemaProcessor.setStorageGroup(new PartialPath("root.vehicle")); @@ -619,20 +619,20 @@ public void testCheckStorageExistOfPath() { schemaProcessor.getBelongedStorageGroups(new PartialPath("root.vehicle")).isEmpty()); assertFalse( schemaProcessor - .getBelongedStorageGroups(new PartialPath("root.vehicle.device")) + .getBelongedStorageGroups(new PartialPath("root.vehicle.device0")) .isEmpty()); assertFalse( schemaProcessor - .getBelongedStorageGroups(new PartialPath("root.vehicle.device.sensor")) + .getBelongedStorageGroups(new PartialPath("root.vehicle.device0.sensor")) .isEmpty()); assertTrue( schemaProcessor.getBelongedStorageGroups(new PartialPath("root.vehicle1")).isEmpty()); assertTrue( schemaProcessor - .getBelongedStorageGroups(new PartialPath("root.vehicle1.device")) + .getBelongedStorageGroups(new PartialPath("root.vehicle1.device0")) .isEmpty()); - schemaProcessor.setStorageGroup(new PartialPath("root.vehicle1.device")); + schemaProcessor.setStorageGroup(new PartialPath("root.vehicle1.device0")); assertTrue( schemaProcessor .getBelongedStorageGroups(new PartialPath("root.vehicle1.device1")) @@ -647,7 +647,7 @@ public void testCheckStorageExistOfPath() { .isEmpty()); assertFalse( schemaProcessor - .getBelongedStorageGroups(new PartialPath("root.vehicle1.device")) + .getBelongedStorageGroups(new PartialPath("root.vehicle1.device0")) .isEmpty()); } catch (MetadataException e) { e.printStackTrace(); @@ -731,58 +731,6 @@ public void testGetStorageGroupNameByAutoLevel() { assertTrue(caughtException); } - @Test - public void testSetStorageGroupWithIllegalName() { - LocalSchemaProcessor schemaProcessor = IoTDB.schemaProcessor; - try { - PartialPath path1 = new PartialPath("root.laptop\n"); - try { - schemaProcessor.setStorageGroup(path1); - fail(); - } catch (MetadataException e) { - } - } catch (IllegalPathException e1) { - fail(); - } - try { - PartialPath path2 = new PartialPath("root.laptop\t"); - try { - schemaProcessor.setStorageGroup(path2); - fail(); - } catch (MetadataException e) { - } - } catch (IllegalPathException e1) { - fail(); - } - } - - @Test - public void testCreateTimeseriesWithIllegalName() { - LocalSchemaProcessor schemaProcessor = IoTDB.schemaProcessor; - try { - PartialPath path1 = new PartialPath("root.laptop.d1\n.s1"); - try { - schemaProcessor.createTimeseries( - path1, TSDataType.INT32, TSEncoding.PLAIN, CompressionType.SNAPPY, null); - fail(); - } catch (MetadataException e) { - } - } catch (IllegalPathException e1) { - fail(); - } - try { - PartialPath path2 = new PartialPath("root.laptop.d1\t.s1"); - try { - schemaProcessor.createTimeseries( - path2, TSDataType.INT32, TSEncoding.PLAIN, CompressionType.SNAPPY, null); - fail(); - } catch (MetadataException e) { - } - } catch (IllegalPathException e1) { - fail(); - } - } - @Test public void testGetDevicesWithGivenPrefix() { LocalSchemaProcessor schemaProcessor = IoTDB.schemaProcessor; @@ -1083,16 +1031,16 @@ public void testTemplateInnerTree() throws MetadataException { assertNull(template.getPathNodeInTemplate("notExists")); assertEquals("[GPS]", template.getAllAlignedPrefix().toString()); - String[] alignedMeasurements = {"to.be.prefix.s1", "to.be.prefix.s2"}; + String[] alignedMeasurements = {"`to`.be.prefix.s1", "`to`.be.prefix.s2"}; TSDataType[] dataTypes = {TSDataType.INT32, TSDataType.INT32}; TSEncoding[] encodings = {TSEncoding.RLE, TSEncoding.RLE}; CompressionType[] compressionTypes = {CompressionType.SNAPPY, CompressionType.SNAPPY}; template.addAlignedMeasurements(alignedMeasurements, dataTypes, encodings, compressionTypes); assertEquals("[to.be.prefix, GPS]", template.getAllAlignedPrefix().toString()); - assertEquals("[s1, s2]", template.getAlignedMeasurements("to.be.prefix").toString()); + assertEquals("[s1, s2]", template.getAlignedMeasurements("`to`.be.prefix").toString()); - template.deleteAlignedPrefix("to.be.prefix"); + template.deleteAlignedPrefix("`to`.be.prefix"); assertEquals("[GPS]", template.getAllAlignedPrefix().toString()); assertEquals(null, template.getDirectNode("prefix")); @@ -1119,7 +1067,7 @@ public void testTemplateInnerTree() throws MetadataException { "[d1.s1, GPS.x, to.be.prefix.s2, GPS.y, to.be.prefix.s1, s2]", template.getAllMeasurementsPaths().toString()); - template.deleteSeriesCascade("to"); + template.deleteSeriesCascade("`to`"); assertEquals("[d1.s1, GPS.x, GPS.y, s2]", template.getAllMeasurementsPaths().toString()); } @@ -1233,8 +1181,8 @@ public void testUnsetSchemaTemplate() throws MetadataException { dataTypeList, encodingList, compressionTypes); - SetTemplatePlan setTemplatePlan = new SetTemplatePlan("template1", "root.sg.1"); - UnsetTemplatePlan unsetTemplatePlan = new UnsetTemplatePlan("root.sg.1", "template1"); + SetTemplatePlan setTemplatePlan = new SetTemplatePlan("template1", "root.sg.`1`"); + UnsetTemplatePlan unsetTemplatePlan = new UnsetTemplatePlan("root.sg.`1`", "template1"); LocalSchemaProcessor schemaProcessor = IoTDB.schemaProcessor; schemaProcessor.createSchemaTemplate(createTemplatePlan); @@ -1243,7 +1191,7 @@ public void testUnsetSchemaTemplate() throws MetadataException { schemaProcessor.unsetSchemaTemplate(unsetTemplatePlan); fail("No exception thrown."); } catch (Exception e) { - assertEquals("Path [root.sg.1] does not exist", e.getMessage()); + assertEquals("Path [root.sg.`1`] does not exist", e.getMessage()); } schemaProcessor.setSchemaTemplate(setTemplatePlan); @@ -1258,7 +1206,7 @@ public void testUnsetSchemaTemplate() throws MetadataException { schemaProcessor.unsetSchemaTemplate(unsetTemplatePlan); fail("No exception thrown."); } catch (Exception e) { - assertEquals("NO template on root.sg.1", e.getMessage()); + assertEquals("NO template on root.sg.`1`", e.getMessage()); } } @@ -1725,18 +1673,6 @@ public void testShowTimeseriesWithTemplate() { e.printStackTrace(); fail(e.getMessage()); } - - // show timeseries root.laptop.d1.(s0,s1) - try { - ShowTimeSeriesPlan showTimeSeriesPlan = - new ShowTimeSeriesPlan( - new PartialPath("root.laptop.d1.(s0,s1)"), false, null, null, 0, 0, false); - schemaProcessor.showTimeseries(showTimeSeriesPlan, EnvironmentUtils.TEST_QUERY_CONTEXT); - } catch (MetadataException e) { - assertEquals( - "Cannot get node of children in different aligned timeseries (Path: (s0,s1))", - e.getMessage()); - } } @Test @@ -1999,16 +1935,16 @@ public void testStorageGroupNameWithHyphen() throws IllegalPathException { LocalSchemaProcessor schemaProcessor = IoTDB.schemaProcessor; assertTrue(schemaProcessor.isPathExist(new PartialPath("root"))); - assertFalse(schemaProcessor.isPathExist(new PartialPath("root.group-with-hyphen"))); + assertFalse(schemaProcessor.isPathExist(new PartialPath("root.group_with_hyphen"))); try { - schemaProcessor.setStorageGroup(new PartialPath("root.group-with-hyphen")); + schemaProcessor.setStorageGroup(new PartialPath("root.group_with_hyphen")); } catch (MetadataException e) { e.printStackTrace(); fail(e.getMessage()); } - assertTrue(schemaProcessor.isPathExist(new PartialPath("root.group-with-hyphen"))); + assertTrue(schemaProcessor.isPathExist(new PartialPath("root.group_with_hyphen"))); } @Test @@ -2288,7 +2224,7 @@ public void testCreateAlignedTimeseriesWithIllegalNames() throws Exception { public void testCreateAlignedTimeseriesWithAliasAndTags() throws Exception { LocalSchemaProcessor schemaProcessor = IoTDB.schemaProcessor; schemaProcessor.setStorageGroup(new PartialPath("root.laptop")); - PartialPath devicePath = new PartialPath("root.laptop.device"); + PartialPath devicePath = new PartialPath("root.laptop.device0"); List measurements = Arrays.asList("s1", "s2", "s3", "s4", "s5"); List tsDataTypes = Arrays.asList( @@ -2333,8 +2269,8 @@ public void testCreateAlignedTimeseriesWithAliasAndTags() throws Exception { schemaProcessor.createAlignedTimeSeries(createAlignedTimeSeriesPlan); Assert.assertEquals( - 5, schemaProcessor.getAllTimeseriesCount(new PartialPath("root.laptop.device.*"))); - Assert.assertTrue(schemaProcessor.isPathExist(new PartialPath("root.laptop.device.alias2"))); + 5, schemaProcessor.getAllTimeseriesCount(new PartialPath("root.laptop.device0.*"))); + Assert.assertTrue(schemaProcessor.isPathExist(new PartialPath("root.laptop.device0.alias2"))); ShowTimeSeriesPlan showTimeSeriesPlan = new ShowTimeSeriesPlan(new PartialPath("root.**"), false, "key", "value", 0, 0, false); @@ -2346,11 +2282,11 @@ public void testCreateAlignedTimeseriesWithAliasAndTags() throws Exception { .sorted(Comparator.comparing(ShowResult::getName)) .collect(Collectors.toList()); ShowTimeSeriesResult result = showTimeSeriesResults.get(0); - Assert.assertEquals("root.laptop.device.s1", result.getName()); + Assert.assertEquals("root.laptop.device0.s1", result.getName()); Assert.assertEquals("alias1", result.getAlias()); Assert.assertEquals(tags, result.getTag()); result = showTimeSeriesResults.get(1); - Assert.assertEquals("root.laptop.device.s4", result.getName()); + Assert.assertEquals("root.laptop.device0.s4", result.getName()); Assert.assertEquals(tags, result.getTag()); } diff --git a/server/src/test/java/org/apache/iotdb/db/metadata/mtree/MTreeAboveSGTest.java b/server/src/test/java/org/apache/iotdb/db/metadata/mtree/MTreeAboveSGTest.java index 4b8140a3f8cd..e357b9a0c05d 100644 --- a/server/src/test/java/org/apache/iotdb/db/metadata/mtree/MTreeAboveSGTest.java +++ b/server/src/test/java/org/apache/iotdb/db/metadata/mtree/MTreeAboveSGTest.java @@ -175,8 +175,8 @@ public void testCheckStorageGroup() { assertTrue(root.isStorageGroup(new PartialPath("root.laptop.d2"))); assertFalse(root.isStorageGroup(new PartialPath("root.laptop.d3"))); - root.setStorageGroup(new PartialPath("root.1")); - assertTrue(root.isStorageGroup(new PartialPath("root.1"))); + root.setStorageGroup(new PartialPath("root.`1`")); + assertTrue(root.isStorageGroup(new PartialPath("root.`1`"))); } catch (MetadataException e) { e.printStackTrace(); fail(e.getMessage()); @@ -209,23 +209,24 @@ public void testCheckStorageExistOfPath() { try { assertTrue(root.getBelongedStorageGroups(new PartialPath("root")).isEmpty()); assertTrue(root.getBelongedStorageGroups(new PartialPath("root.vehicle")).isEmpty()); - assertTrue(root.getBelongedStorageGroups(new PartialPath("root.vehicle.device")).isEmpty()); + assertTrue(root.getBelongedStorageGroups(new PartialPath("root.vehicle.device0")).isEmpty()); assertTrue( - root.getBelongedStorageGroups(new PartialPath("root.vehicle.device.sensor")).isEmpty()); + root.getBelongedStorageGroups(new PartialPath("root.vehicle.device0.sensor")).isEmpty()); root.setStorageGroup(new PartialPath("root.vehicle")); assertFalse(root.getBelongedStorageGroups(new PartialPath("root.vehicle")).isEmpty()); - assertFalse(root.getBelongedStorageGroups(new PartialPath("root.vehicle.device")).isEmpty()); + assertFalse(root.getBelongedStorageGroups(new PartialPath("root.vehicle.device0")).isEmpty()); assertFalse( - root.getBelongedStorageGroups(new PartialPath("root.vehicle.device.sensor")).isEmpty()); + root.getBelongedStorageGroups(new PartialPath("root.vehicle.device0.sensor")).isEmpty()); assertTrue(root.getBelongedStorageGroups(new PartialPath("root.vehicle1")).isEmpty()); - assertTrue(root.getBelongedStorageGroups(new PartialPath("root.vehicle1.device")).isEmpty()); + assertTrue(root.getBelongedStorageGroups(new PartialPath("root.vehicle1.device0")).isEmpty()); - root.setStorageGroup(new PartialPath("root.vehicle1.device")); + root.setStorageGroup(new PartialPath("root.vehicle1.device0")); assertTrue(root.getBelongedStorageGroups(new PartialPath("root.vehicle1.device1")).isEmpty()); assertTrue(root.getBelongedStorageGroups(new PartialPath("root.vehicle1.device2")).isEmpty()); assertTrue(root.getBelongedStorageGroups(new PartialPath("root.vehicle1.device3")).isEmpty()); - assertFalse(root.getBelongedStorageGroups(new PartialPath("root.vehicle1.device")).isEmpty()); + assertFalse( + root.getBelongedStorageGroups(new PartialPath("root.vehicle1.device0")).isEmpty()); } catch (MetadataException e) { e.printStackTrace(); fail(e.getMessage()); @@ -237,9 +238,7 @@ public void testIllegalStorageGroup() { try { root.setStorageGroup(new PartialPath("root.\"sg.ln\"")); } catch (MetadataException e) { - Assert.assertEquals( - "The storage group name can only be characters, numbers and underscores. root.\"sg.ln\" is not a legal path", - e.getMessage()); + Assert.assertEquals("root.\"sg.ln\" is not a legal path", e.getMessage()); } } diff --git a/server/src/test/java/org/apache/iotdb/db/metadata/mtree/schemafile/SchemaFileTest.java b/server/src/test/java/org/apache/iotdb/db/metadata/mtree/schemafile/SchemaFileTest.java index 98b1650464cd..9c77a0777714 100644 --- a/server/src/test/java/org/apache/iotdb/db/metadata/mtree/schemafile/SchemaFileTest.java +++ b/server/src/test/java/org/apache/iotdb/db/metadata/mtree/schemafile/SchemaFileTest.java @@ -640,7 +640,7 @@ private IMeasurementSchema getSchema(String id) { } private IMNode getNode(IMNode root, String path) throws MetadataException { - String[] pathNodes = PathUtils.splitPathToDetachedPath(path); + String[] pathNodes = PathUtils.splitPathToDetachedNodes(path); IMNode cur = root; for (String node : pathNodes) { if (!node.equals("root")) { @@ -685,7 +685,7 @@ private static long getSegAddrInContainer(IMNode par) { // region Tree Constructor private IMNode virtualTriangleMTree(int size, String sgPath) throws MetadataException { - String[] sgPathNodes = PathUtils.splitPathToDetachedPath(sgPath); + String[] sgPathNodes = PathUtils.splitPathToDetachedNodes(sgPath); IMNode upperNode = null; for (String name : sgPathNodes) { IMNode child = new InternalMNode(upperNode, name); diff --git a/server/src/test/java/org/apache/iotdb/db/metadata/upgrade/MetadataUpgradeTest.java b/server/src/test/java/org/apache/iotdb/db/metadata/upgrade/MetadataUpgradeTest.java index f37f2e034c32..2faad460fb44 100644 --- a/server/src/test/java/org/apache/iotdb/db/metadata/upgrade/MetadataUpgradeTest.java +++ b/server/src/test/java/org/apache/iotdb/db/metadata/upgrade/MetadataUpgradeTest.java @@ -245,7 +245,7 @@ private CreateTemplatePlan getCreateTemplatePlan(String templateName, String mea } private CreateAlignedTimeSeriesPlan getCreateAlignedTimeseriesPlan() throws IllegalPathException { - PartialPath devicePath = new PartialPath("root.unsetTemplate1.sg1.device"); + PartialPath devicePath = new PartialPath("root.unsetTemplate1.sg1.device0"); List measurements = Arrays.asList("s1", "s2", "s3", "s4", "s5"); List tsDataTypes = Arrays.asList( diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/metadata/read/DeviceSchemaScanNodeSerdeTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/metadata/read/DeviceSchemaScanNodeSerdeTest.java index 5a98d40f1665..a2afa4e721f9 100644 --- a/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/metadata/read/DeviceSchemaScanNodeSerdeTest.java +++ b/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/metadata/read/DeviceSchemaScanNodeSerdeTest.java @@ -48,7 +48,7 @@ public void testSerializeAndDeserialize() throws IllegalPathException { DevicesSchemaScanNode devicesSchemaScanNode = new DevicesSchemaScanNode( new PlanNodeId("deviceSchemaScan"), - new PartialPath("root.sg.device"), + new PartialPath("root.sg.device0"), 10, 10, false, diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/metadata/read/SchemaCountNodeSerdeTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/metadata/read/SchemaCountNodeSerdeTest.java index b600548b6103..532fe6fdf4da 100644 --- a/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/metadata/read/SchemaCountNodeSerdeTest.java +++ b/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/metadata/read/SchemaCountNodeSerdeTest.java @@ -44,7 +44,7 @@ public void testDevicesCountSerializeAndDeserialize() throws IllegalPathExceptio ExchangeNode exchangeNode = new ExchangeNode(new PlanNodeId("exchange")); DevicesCountNode devicesCountNode = new DevicesCountNode( - new PlanNodeId("devicesCount"), new PartialPath("root.sg.device"), true); + new PlanNodeId("devicesCount"), new PartialPath("root.sg.device0"), true); FragmentSinkNode fragmentSinkNode = new FragmentSinkNode(new PlanNodeId("fragmentSink")); fragmentSinkNode.addChild(devicesCountNode); fragmentSinkNode.setDownStream( @@ -70,7 +70,7 @@ public void testTimeSeriesCountSerializeAndDeserialize() throws IllegalPathExcep ExchangeNode exchangeNode = new ExchangeNode(new PlanNodeId("exchange")); LevelTimeSeriesCountNode levelTimeSeriesCountNode = new LevelTimeSeriesCountNode( - new PlanNodeId("timeseriesCount"), new PartialPath("root.sg.device"), true, 10); + new PlanNodeId("timeseriesCount"), new PartialPath("root.sg.device0"), true, 10); FragmentSinkNode fragmentSinkNode = new FragmentSinkNode(new PlanNodeId("fragmentSink")); fragmentSinkNode.addChild(levelTimeSeriesCountNode); fragmentSinkNode.setDownStream( diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/metadata/read/TimeSeriesSchemaScanNodeSerdeTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/metadata/read/TimeSeriesSchemaScanNodeSerdeTest.java index d4898d720092..564ba6491674 100644 --- a/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/metadata/read/TimeSeriesSchemaScanNodeSerdeTest.java +++ b/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/metadata/read/TimeSeriesSchemaScanNodeSerdeTest.java @@ -48,7 +48,7 @@ public void testSerializeAndDeserialize() throws IllegalPathException { TimeSeriesSchemaScanNode timeSeriesSchemaScanNode = new TimeSeriesSchemaScanNode( new PlanNodeId("timeSeriesSchemaScan"), - new PartialPath("root.sg.device.sensor"), + new PartialPath("root.sg.device0.sensor"), null, null, 10, diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/sink/FragmentSinkNodeSerdeTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/sink/FragmentSinkNodeSerdeTest.java index f92f55e3b0c8..9d16d18065f3 100644 --- a/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/sink/FragmentSinkNodeSerdeTest.java +++ b/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/sink/FragmentSinkNodeSerdeTest.java @@ -42,7 +42,12 @@ public void testSerializeAndDeserialize() throws IllegalPathException { new FragmentSinkNode(new PlanNodeId("TestFragmentSinkNode")); fragmentSinkNode.addChild( new DevicesSchemaScanNode( - new PlanNodeId("deviceSchema"), new PartialPath("root.sg.device"), 0, 0, false, false)); + new PlanNodeId("deviceSchema"), + new PartialPath("root.sg.device0"), + 0, + 0, + false, + false)); fragmentSinkNode.setDownStream( new TEndPoint("127.0.0.1", 6666), new FragmentInstanceId(new PlanFragmentId("q", 1), "ds"), diff --git a/server/src/test/java/org/apache/iotdb/db/qp/physical/PhysicalPlanSerializeTest.java b/server/src/test/java/org/apache/iotdb/db/qp/physical/PhysicalPlanSerializeTest.java index ff2dc0843daa..f313e048d628 100644 --- a/server/src/test/java/org/apache/iotdb/db/qp/physical/PhysicalPlanSerializeTest.java +++ b/server/src/test/java/org/apache/iotdb/db/qp/physical/PhysicalPlanSerializeTest.java @@ -258,13 +258,9 @@ public void createMuSerializeTest2() throws IOException, IllegalPathException { @Test public void createMuSerializeTest3() throws IOException, IllegalPathException { - // same as: - // create timeseries root.sg.d1.s0 with datatype=DOUBLE, encoding=GORILLA, compression=SNAPPY - // create aligned timeseries root.sg.d1.(s1 INT64, s2 DOUBLE, s3 INT64) - // with encoding=(GORILLA, GORILLA, GORILLA), compression=SNAPPY CreateMultiTimeSeriesPlan plan = new CreateMultiTimeSeriesPlan(); plan.setPaths( - Arrays.asList(new PartialPath("root.sg.d1.s0"), new PartialPath("root.sg.d1.(s1,s2,s3)"))); + Arrays.asList(new PartialPath("root.sg.d1.s0"), new PartialPath("root.sg.d1.s1"))); plan.setDataTypes( Arrays.asList(TSDataType.DOUBLE, TSDataType.INT64, TSDataType.DOUBLE, TSDataType.INT64)); plan.setEncodings( diff --git a/tsfile/pom.xml b/tsfile/pom.xml index 80cda0787881..c3b2b1deaa6c 100644 --- a/tsfile/pom.xml +++ b/tsfile/pom.xml @@ -49,6 +49,10 @@ commons-io commons-io + + org.apache.commons + commons-lang3 + org.lz4 lz4-java @@ -98,6 +102,12 @@ 4.0.3 test + + + org.apache.iotdb + iotdb-antlr + ${project.version} + diff --git a/tsfile/src/main/java/org/apache/iotdb/tsfile/common/constant/TsFileConstant.java b/tsfile/src/main/java/org/apache/iotdb/tsfile/common/constant/TsFileConstant.java index be8ecb3d5e4a..9b496b944554 100644 --- a/tsfile/src/main/java/org/apache/iotdb/tsfile/common/constant/TsFileConstant.java +++ b/tsfile/src/main/java/org/apache/iotdb/tsfile/common/constant/TsFileConstant.java @@ -18,6 +18,8 @@ */ package org.apache.iotdb.tsfile.common.constant; +import java.util.regex.Pattern; + public class TsFileConstant { public static final String TSFILE_SUFFIX = ".tsfile"; @@ -30,9 +32,14 @@ public class TsFileConstant { public static final String PATH_SEPARATER_NO_REGEX = "\\."; public static final char DOUBLE_QUOTE = '"'; public static final char BACK_QUOTE = '`'; + public static final String BACK_QUOTE_STRING = "`"; + public static final String DOUBLE_BACK_QUOTE_STRING = "``"; public static final byte TIME_COLUMN_MASK = (byte) 0x80; public static final byte VALUE_COLUMN_MASK = (byte) 0x40; + private static final String NODE_NAME_MATCHER = "([a-zA-Z0-9_:@#${}\\u2E80-\\u9FFF]+)"; + public static final Pattern NODE_NAME_PATTERN = Pattern.compile(NODE_NAME_MATCHER); + private TsFileConstant() {} } diff --git a/tsfile/src/main/java/org/apache/iotdb/tsfile/exception/PathParseException.java b/tsfile/src/main/java/org/apache/iotdb/tsfile/exception/PathParseException.java new file mode 100644 index 000000000000..37f6cf2fbeeb --- /dev/null +++ b/tsfile/src/main/java/org/apache/iotdb/tsfile/exception/PathParseException.java @@ -0,0 +1,26 @@ +/* + * 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.iotdb.tsfile.exception; + +public class PathParseException extends TsFileRuntimeException { + + public PathParseException(String path) { + super(String.format("%s is not a legal path.", path)); + } +} diff --git a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/Path.java b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/Path.java index 5ac56c2473eb..4bd940b30d3a 100644 --- a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/Path.java +++ b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/Path.java @@ -19,8 +19,13 @@ package org.apache.iotdb.tsfile.read.common; import org.apache.iotdb.tsfile.common.constant.TsFileConstant; +import org.apache.iotdb.tsfile.exception.PathParseException; +import org.apache.iotdb.tsfile.read.common.parser.PathNodesGenerator; import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; + import java.io.Serializable; import java.nio.ByteBuffer; @@ -58,40 +63,20 @@ public Path(String pathSc) { */ public Path(String pathSc, boolean needSplit) { if (pathSc == null) { - throw new IllegalArgumentException(ILLEGAL_PATH_ARGUMENT); + throw new PathParseException(ILLEGAL_PATH_ARGUMENT); } if (!needSplit) { + // no split, we don't use antlr to check here. fullPath = pathSc; } else { if (pathSc.length() > 0) { - if (pathSc.charAt(pathSc.length() - 1) == TsFileConstant.DOUBLE_QUOTE) { - int endIndex = pathSc.lastIndexOf('"', pathSc.length() - 2); - // if a double quotes with escape character - while (endIndex != -1 && pathSc.charAt(endIndex - 1) == '\\') { - endIndex = pathSc.lastIndexOf('"', endIndex - 2); - } - if (endIndex != -1 && (endIndex == 0 || pathSc.charAt(endIndex - 1) == '.')) { - fullPath = pathSc; - device = pathSc.substring(0, endIndex - 1); - measurement = pathSc.substring(endIndex); - } else { - throw new IllegalArgumentException(ILLEGAL_PATH_ARGUMENT); - } - } else if (pathSc.charAt(pathSc.length() - 1) != TsFileConstant.DOUBLE_QUOTE - && pathSc.charAt(pathSc.length() - 1) != TsFileConstant.PATH_SEPARATOR_CHAR) { - int endIndex = pathSc.lastIndexOf(TsFileConstant.PATH_SEPARATOR_CHAR); - if (endIndex < 0) { - fullPath = pathSc; - device = ""; - measurement = pathSc; - } else { - fullPath = pathSc; - device = pathSc.substring(0, endIndex); - measurement = pathSc.substring(endIndex + 1); - } - } else { - throw new IllegalArgumentException(ILLEGAL_PATH_ARGUMENT); + String[] nodes = PathNodesGenerator.splitPathToNodes(pathSc); + device = ""; + if (nodes.length > 1) { + device = transformNodesToString(nodes, nodes.length - 1); } + measurement = nodes[nodes.length - 1]; + fullPath = transformNodesToString(nodes, nodes.length); } else { fullPath = pathSc; device = ""; @@ -108,14 +93,29 @@ public Path(String pathSc, boolean needSplit) { */ public Path(String device, String measurement) { if (device == null || measurement == null) { - throw new IllegalArgumentException(ILLEGAL_PATH_ARGUMENT); + throw new PathParseException(ILLEGAL_PATH_ARGUMENT); } - this.device = device; - this.measurement = measurement; - if (!"".equals(device)) { - this.fullPath = device + TsFileConstant.PATH_SEPARATOR + measurement; + // use PathNodesGenerator to check whether path is legal. + if (!StringUtils.isEmpty(device) && !StringUtils.isEmpty(measurement)) { + String path = device + TsFileConstant.PATH_SEPARATOR + measurement; + String[] nodes = PathNodesGenerator.splitPathToNodes(path); + this.device = transformNodesToString(nodes, nodes.length - 1); + this.measurement = nodes[nodes.length - 1]; + this.fullPath = transformNodesToString(nodes, nodes.length); + } else if (!StringUtils.isEmpty(device)) { + String[] deviceNodes = PathNodesGenerator.splitPathToNodes(device); + this.device = transformNodesToString(deviceNodes, deviceNodes.length); + this.measurement = measurement; + this.fullPath = device; + } else if (!StringUtils.isEmpty(measurement)) { + String[] measurementNodes = PathNodesGenerator.splitPathToNodes(measurement); + this.measurement = transformNodesToString(measurementNodes, measurementNodes.length); + this.device = device; + this.fullPath = measurement; } else { - fullPath = measurement; + this.device = device; + this.measurement = measurement; + this.fullPath = ""; } } @@ -191,4 +191,14 @@ public static Path deserialize(ByteBuffer byteBuffer) { path.fullPath = ReadWriteIOUtils.readString(byteBuffer); return path; } + + private String transformNodesToString(String[] nodes, int index) { + Validate.isTrue(nodes.length > 0); + StringBuilder s = new StringBuilder(nodes[0]); + for (int i = 1; i < index; i++) { + s.append(TsFileConstant.PATH_SEPARATOR); + s.append(nodes[i]); + } + return s.toString(); + } } diff --git a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/parser/PathNodesGenerator.java b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/parser/PathNodesGenerator.java new file mode 100644 index 000000000000..30033ef65d3a --- /dev/null +++ b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/parser/PathNodesGenerator.java @@ -0,0 +1,94 @@ +/* + * 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.iotdb.tsfile.read.common.parser; + +import org.apache.iotdb.db.qp.sql.PathParser; +import org.apache.iotdb.db.qp.sql.SqlLexer; +import org.apache.iotdb.tsfile.exception.PathParseException; + +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.atn.PredictionMode; +import org.antlr.v4.runtime.misc.ParseCancellationException; +import org.antlr.v4.runtime.tree.ParseTree; + +/** convert String path to String[] nodes * */ +public class PathNodesGenerator { + private static PathVisitor pathVisitor = new PathVisitor(); + + public static String[] splitPathToNodes(String path) throws PathParseException { + try { + return invokeParser(path); + } catch (ParseCancellationException e) { + throw new PathParseException(path); + } + } + + /** throw exception if path is illegal. */ + public static void checkPath(String path) throws PathParseException { + try { + invokeParser(path); + } catch (ParseCancellationException e) { + throw new PathParseException(path); + } + } + + private static String[] invokeParser(String path) { + + CharStream charStream1 = CharStreams.fromString(path); + + SqlLexer lexer1 = new SqlLexer(charStream1); + lexer1.removeErrorListeners(); + lexer1.addErrorListener(PathParseError.INSTANCE); + + CommonTokenStream tokens1 = new CommonTokenStream(lexer1); + + PathParser pathParser1 = new PathParser(tokens1); + pathParser1.getInterpreter().setPredictionMode(PredictionMode.SLL); + pathParser1.removeErrorListeners(); + pathParser1.addErrorListener(PathParseError.INSTANCE); + + ParseTree tree; + try { + // STAGE 1: try with simpler/faster SLL(*) + tree = pathParser1.path(); + // if we get here, there was no syntax error and SLL(*) was enough; + // there is no need to try full LL(*) + } catch (Exception ex) { + CharStream charStream2 = CharStreams.fromString(path); + + SqlLexer lexer2 = new SqlLexer(charStream2); + lexer2.removeErrorListeners(); + lexer2.addErrorListener(PathParseError.INSTANCE); + + CommonTokenStream tokens2 = new CommonTokenStream(lexer2); + + PathParser pathParser2 = new PathParser(tokens2); + pathParser2.getInterpreter().setPredictionMode(PredictionMode.LL); + pathParser2.removeErrorListeners(); + pathParser2.addErrorListener(PathParseError.INSTANCE); + + // STAGE 2: parser with full LL(*) + tree = pathParser2.path(); + // if we get here, it's LL not SLL + } + return pathVisitor.visit(tree); + } +} diff --git a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/parser/PathParseError.java b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/parser/PathParseError.java new file mode 100644 index 000000000000..c29000b86764 --- /dev/null +++ b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/parser/PathParseError.java @@ -0,0 +1,39 @@ +/* + * 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.iotdb.tsfile.read.common.parser; + +import org.antlr.v4.runtime.BaseErrorListener; +import org.antlr.v4.runtime.RecognitionException; +import org.antlr.v4.runtime.Recognizer; +import org.antlr.v4.runtime.misc.ParseCancellationException; + +public class PathParseError extends BaseErrorListener { + public static final PathParseError INSTANCE = new PathParseError(); + + @Override + public void syntaxError( + Recognizer recognizer, + Object offendingSymbol, + int line, + int charPositionInLine, + String msg, + RecognitionException e) { + throw new ParseCancellationException("line " + line + ":" + charPositionInLine + " " + msg); + } +} diff --git a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/parser/PathVisitor.java b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/parser/PathVisitor.java new file mode 100644 index 000000000000..8ab23507a363 --- /dev/null +++ b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/parser/PathVisitor.java @@ -0,0 +1,75 @@ +/* + * 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.iotdb.tsfile.read.common.parser; + +import org.apache.iotdb.db.qp.sql.PathParser; +import org.apache.iotdb.db.qp.sql.PathParser.NodeNameContext; +import org.apache.iotdb.db.qp.sql.PathParserBaseVisitor; +import org.apache.iotdb.tsfile.common.constant.TsFileConstant; + +import org.apache.commons.lang3.StringUtils; + +import java.util.List; + +public class PathVisitor extends PathParserBaseVisitor { + + @Override + public String[] visitPath(PathParser.PathContext ctx) { + if (ctx.prefixPath() != null) { + return visitPrefixPath(ctx.prefixPath()); + } else { + return visitSuffixPath(ctx.suffixPath()); + } + } + + @Override + public String[] visitPrefixPath(PathParser.PrefixPathContext ctx) { + List nodeNames = ctx.nodeName(); + String[] path = new String[nodeNames.size() + 1]; + path[0] = ctx.ROOT().getText(); + for (int i = 0; i < nodeNames.size(); i++) { + path[i + 1] = parseNodeName(nodeNames.get(i)); + } + return path; + } + + @Override + public String[] visitSuffixPath(PathParser.SuffixPathContext ctx) { + List nodeNames = ctx.nodeName(); + String[] path = new String[nodeNames.size()]; + for (int i = 0; i < nodeNames.size(); i++) { + path[i] = parseNodeName(nodeNames.get(i)); + } + return path; + } + + private String parseNodeName(PathParser.NodeNameContext ctx) { + String nodeName = ctx.getText(); + if (nodeName.startsWith(TsFileConstant.BACK_QUOTE_STRING) + && nodeName.endsWith(TsFileConstant.BACK_QUOTE_STRING)) { + String unWrapped = nodeName.substring(1, nodeName.length() - 1); + if (StringUtils.isNumeric(unWrapped) + || !TsFileConstant.NODE_NAME_PATTERN.matcher(unWrapped).matches()) { + return nodeName; + } + return unWrapped; + } + return nodeName; + } +} diff --git a/tsfile/src/test/java/org/apache/iotdb/tsfile/read/common/PathTest.java b/tsfile/src/test/java/org/apache/iotdb/tsfile/read/common/PathTest.java index 2cfbcced962f..f80f369cfecb 100644 --- a/tsfile/src/test/java/org/apache/iotdb/tsfile/read/common/PathTest.java +++ b/tsfile/src/test/java/org/apache/iotdb/tsfile/read/common/PathTest.java @@ -18,32 +18,161 @@ */ package org.apache.iotdb.tsfile.read.common; +import org.apache.iotdb.tsfile.exception.PathParseException; + import org.junit.Assert; import org.junit.Test; +import static org.junit.Assert.fail; + public class PathTest { @Test - public void testPath() { + public void testLegalPath() { + // empty path Path a = new Path("", true); Assert.assertEquals("", a.getDevice()); Assert.assertEquals("", a.getMeasurement()); - Path b = new Path("root.\"sg\".\"d1\".\"s1\"", true); - Assert.assertEquals("root.\"sg\".\"d1\"", b.getDevice()); - Assert.assertEquals("\"s1\"", b.getMeasurement()); - Path c = new Path("root.\"sg\".\"d1\".s1", true); - Assert.assertEquals("root.\"sg\".\"d1\"", c.getDevice()); - Assert.assertEquals("s1", c.getMeasurement()); - Path d = new Path("s1", true); - Assert.assertEquals("s1", d.getMeasurement()); - Assert.assertEquals("", d.getDevice()); - Path e = new Path("root.\"s.g\".d1.\"s..\\\"s1\"", true); - Assert.assertEquals("root.\"s.g\".d1", e.getDevice()); - Assert.assertEquals("\"s..\\\"s1\"", e.getMeasurement()); + + // empty device + Path b = new Path("s1", true); + Assert.assertEquals("s1", b.getMeasurement()); + Assert.assertEquals("", b.getDevice()); + + // normal node + Path c = new Path("root.sg.a", true); + Assert.assertEquals("root.sg", c.getDevice()); + Assert.assertEquals("a", c.getMeasurement()); + + // quoted node + Path d = new Path("root.sg.`a.b`", true); + Assert.assertEquals("root.sg", d.getDevice()); + Assert.assertEquals("`a.b`", d.getMeasurement()); + + Path e = new Path("root.sg.`a.``b`", true); + Assert.assertEquals("root.sg", e.getDevice()); + Assert.assertEquals("`a.``b`", e.getMeasurement()); + + Path f = new Path("root.`sg\"`.`a.``b`", true); + Assert.assertEquals("root.`sg\"`", f.getDevice()); + Assert.assertEquals("`a.``b`", f.getMeasurement()); + + Path g = new Path("root.sg.`a.b\\\\`", true); + Assert.assertEquals("root.sg", g.getDevice()); + Assert.assertEquals("`a.b\\\\`", g.getMeasurement()); + + // quoted node of digits + Path h = new Path("root.sg.`111`", true); + Assert.assertEquals("root.sg", h.getDevice()); + Assert.assertEquals("`111`", h.getMeasurement()); + + // quoted node of key word + Path i = new Path("root.sg.`select`", true); + Assert.assertEquals("root.sg", i.getDevice()); + Assert.assertEquals("select", i.getMeasurement()); + + // wildcard + Path j = new Path("root.sg.`a*b`", true); + Assert.assertEquals("root.sg", j.getDevice()); + Assert.assertEquals("`a*b`", j.getMeasurement()); + + Path k = new Path("root.sg.*", true); + Assert.assertEquals("root.sg", k.getDevice()); + Assert.assertEquals("*", k.getMeasurement()); + + Path l = new Path("root.sg.**", true); + Assert.assertEquals("root.sg", l.getDevice()); + Assert.assertEquals("**", l.getMeasurement()); + + // raw key word + Path m = new Path("root.sg.select", true); + Assert.assertEquals("root.sg", m.getDevice()); + Assert.assertEquals("select", m.getMeasurement()); + + Path n = new Path("root.sg.device", true); + Assert.assertEquals("root.sg", n.getDevice()); + Assert.assertEquals("device", n.getMeasurement()); + + Path o = new Path("root.sg.drop_trigger", true); + Assert.assertEquals("root.sg", o.getDevice()); + Assert.assertEquals("drop_trigger", o.getMeasurement()); } - @Test(expected = IllegalArgumentException.class) - public void testWrongPath() { - Path c = new Path("root.\"sg\".\"d1\".\"s1\"\"", true); - System.out.println(c.getMeasurement()); + @Test + public void tesIllegalPath() { + try { + new Path("root.sg`", true); + fail(); + } catch (PathParseException ignored) { + + } + + try { + new Path("root.sg\na", true); + fail(); + } catch (PathParseException ignored) { + } + + try { + new Path("root.select`", true); + fail(); + } catch (PathParseException ignored) { + } + + try { + // pure digits + new Path("root.111", true); + fail(); + } catch (PathParseException ignored) { + } + + try { + // single ` in quoted node + new Path("root.`a``", true); + fail(); + } catch (PathParseException ignored) { + } + + try { + // single ` in quoted node + new Path("root.``a`", true); + fail(); + } catch (PathParseException ignored) { + } + + try { + new Path("root.a*%", true); + fail(); + } catch (PathParseException ignored) { + } + + try { + new Path("root.a*b", true); + fail(); + } catch (PathParseException ignored) { + } + + try { + new Path("root.and", true); + fail(); + } catch (PathParseException ignored) { + } + + try { + new Path("root.or", true); + fail(); + } catch (PathParseException ignored) { + } + + try { + new Path("root.not", true); + fail(); + } catch (PathParseException ignored) { + } + + try { + new Path("root.contains", true); + fail(); + } catch (PathParseException ignored) { + } } } From 0378bbe78a8ccc1a5758aec1fc9385f6de1a7bd9 Mon Sep 17 00:00:00 2001 From: Chen YZ <43774645+Cpaulyz@users.noreply.github.com> Date: Wed, 18 May 2022 08:32:52 +0800 Subject: [PATCH 036/436] [IOTDB-3144][IOTDB-3130][IOTDB-3133] Set the default pipe status of receiver after reboot to STOP (#5859) --- .../integration/sync/IoTDBSyncReceiverIT.java | 32 +++++++++++++++++-- .../iotdb/db/sync/conf/SyncConstant.java | 2 +- .../db/sync/receiver/ReceiverService.java | 16 +++++----- .../db/sync/receiver/collector/Collector.java | 2 +- .../recovery/ReceiverLogAnalyzer.java | 6 +++- .../sync/sender/service/TransportHandler.java | 5 +-- .../recovery/ReceiverLogAnalyzerTest.java | 2 +- 7 files changed, 47 insertions(+), 18 deletions(-) diff --git a/integration/src/test/java/org/apache/iotdb/db/integration/sync/IoTDBSyncReceiverIT.java b/integration/src/test/java/org/apache/iotdb/db/integration/sync/IoTDBSyncReceiverIT.java index e66646e8d60e..d2333392dd50 100644 --- a/integration/src/test/java/org/apache/iotdb/db/integration/sync/IoTDBSyncReceiverIT.java +++ b/integration/src/test/java/org/apache/iotdb/db/integration/sync/IoTDBSyncReceiverIT.java @@ -77,7 +77,7 @@ public class IoTDBSyncReceiverIT { String pipeName1 = "pipe1"; String remoteIp1; long createdTime1 = System.currentTimeMillis(); - + String showPipeSql = "SHOW PIPE"; TransportClient client; @Before @@ -149,7 +149,6 @@ public void testStopPipeServerCheck() { public void testPipeOperation() { logger.info("testPipeOperation"); String[] columnNames = {"create time", "name", "role", "remote", "status", "message"}; - String showPipeSql = "SHOW PIPE"; try { // create client.heartbeat(new SyncRequest(RequestType.CREATE, pipeName1, remoteIp1, createdTime1)); @@ -179,6 +178,35 @@ public void testPipeOperation() { "") }; SyncTestUtil.checkResult(showPipeSql, columnNames, retArray, false); + // restart + EnvironmentUtils.shutdownDaemon(); + EnvironmentUtils.reactiveDaemon(); + retArray = + new String[] { + String.format( + "%s,%s,%s,%s,%s,%s", + DatetimeUtils.convertLongToDate(createdTime1), + pipeName1, + "receiver", + remoteIp1, + PipeStatus.STOP.name(), + "") + }; + SyncTestUtil.checkResult(showPipeSql, columnNames, retArray, false); + // start again + client.heartbeat(new SyncRequest(RequestType.START, pipeName1, remoteIp1, createdTime1)); + retArray = + new String[] { + String.format( + "%s,%s,%s,%s,%s,%s", + DatetimeUtils.convertLongToDate(createdTime1), + pipeName1, + "receiver", + remoteIp1, + PipeStatus.RUNNING.name(), + "") + }; + SyncTestUtil.checkResult(showPipeSql, columnNames, retArray, false); // stop client.heartbeat(new SyncRequest(RequestType.STOP, pipeName1, remoteIp1, createdTime1)); retArray = diff --git a/server/src/main/java/org/apache/iotdb/db/sync/conf/SyncConstant.java b/server/src/main/java/org/apache/iotdb/db/sync/conf/SyncConstant.java index 6f24cbdd05d2..c7ad9dcd7dd1 100644 --- a/server/src/main/java/org/apache/iotdb/db/sync/conf/SyncConstant.java +++ b/server/src/main/java/org/apache/iotdb/db/sync/conf/SyncConstant.java @@ -52,7 +52,7 @@ public class SyncConstant { public static final String DEFAULT_PIPE_SINK_IP = "127.0.0.1"; public static final int DEFAULT_PIPE_SINK_PORT = 6670; - public static final Long HEARTBEAT_DELAY_SECONDS = 10 * 60L; + public static final Long HEARTBEAT_DELAY_SECONDS = 30L; public static final int CONNECT_TIMEOUT_MILLISECONDS = 1_000; public static final int SOCKET_TIMEOUT_MILLISECONDS = 100_000; diff --git a/server/src/main/java/org/apache/iotdb/db/sync/receiver/ReceiverService.java b/server/src/main/java/org/apache/iotdb/db/sync/receiver/ReceiverService.java index 1a323ac8542e..08e1d59ee156 100644 --- a/server/src/main/java/org/apache/iotdb/db/sync/receiver/ReceiverService.java +++ b/server/src/main/java/org/apache/iotdb/db/sync/receiver/ReceiverService.java @@ -75,14 +75,6 @@ public synchronized void startPipeServer(boolean isRecovery) throws PipeServerEx TransportServerManager.getInstance().startService(); receiverManager.startServer(); collector.startCollect(); - // recover started pipe - List pipeInfos = receiverManager.getAllPipeInfos(); - for (PipeInfo pipeInfo : pipeInfos) { - if (pipeInfo.getStatus().equals(PipeStatus.RUNNING)) { - collector.startPipe( - pipeInfo.getPipeName(), pipeInfo.getRemoteIp(), pipeInfo.getCreateTime()); - } - } } catch (IOException | StartupException e) { throw new PipeServerException("Failed to start pipe server because " + e.getMessage()); } @@ -109,12 +101,20 @@ public synchronized void stopPipeServer() throws PipeServerException { } } + private void checkPipe(String pipeName, String remoteIp, long createTime) throws IOException { + PipeInfo pipeInfo = receiverManager.getPipeInfo(pipeName, remoteIp, createTime); + if (pipeInfo != null && pipeInfo.getStatus().equals(PipeStatus.STOP)) { + startPipe(pipeName, remoteIp, createTime); + } + } + /** heartbeat RPC handle */ public synchronized SyncResponse receiveMsg(SyncRequest request) { SyncResponse response = new SyncResponse(ResponseType.INFO, ""); try { switch (request.getType()) { case HEARTBEAT: + checkPipe(request.getPipeName(), request.getRemoteIp(), request.getCreateTime()); PipeMessage message = receiverManager.getPipeMessage( request.getPipeName(), request.getRemoteIp(), request.getCreateTime(), true); diff --git a/server/src/main/java/org/apache/iotdb/db/sync/receiver/collector/Collector.java b/server/src/main/java/org/apache/iotdb/db/sync/receiver/collector/Collector.java index cd9cdc08be44..9faeb8df5077 100644 --- a/server/src/main/java/org/apache/iotdb/db/sync/receiver/collector/Collector.java +++ b/server/src/main/java/org/apache/iotdb/db/sync/receiver/collector/Collector.java @@ -134,7 +134,7 @@ public void run() { pipeDataQueue.commit(); logger.info("Commit pipeData with serialize number {}", pipeData.getSerialNumber()); } catch (InterruptedException e) { - logger.warn("Be interrupted when waiting for pipe data, because {}", e.getMessage()); + logger.warn("Be interrupted when waiting for pipe data"); Thread.currentThread().interrupt(); break; } catch (PipeDataLoadBearableException e) { diff --git a/server/src/main/java/org/apache/iotdb/db/sync/receiver/recovery/ReceiverLogAnalyzer.java b/server/src/main/java/org/apache/iotdb/db/sync/receiver/recovery/ReceiverLogAnalyzer.java index 3442c1154c39..8e42e290917e 100644 --- a/server/src/main/java/org/apache/iotdb/db/sync/receiver/recovery/ReceiverLogAnalyzer.java +++ b/server/src/main/java/org/apache/iotdb/db/sync/receiver/recovery/ReceiverLogAnalyzer.java @@ -123,7 +123,11 @@ private void analyzeServiceLog(String logLine) { if (items.length == 4) { // start、stop、drop PipeStatus status = PipeStatus.valueOf(items[3]); - pipeInfos.get(pipeName).get(remoteIp).put(createTime, status); + if (status.equals(PipeStatus.RUNNING)) { + pipeInfos.get(pipeName).get(remoteIp).put(createTime, PipeStatus.STOP); + } else { + pipeInfos.get(pipeName).get(remoteIp).put(createTime, status); + } } else { // create pipeInfos.putIfAbsent(pipeName, new HashMap<>()); diff --git a/server/src/main/java/org/apache/iotdb/db/sync/sender/service/TransportHandler.java b/server/src/main/java/org/apache/iotdb/db/sync/sender/service/TransportHandler.java index f510dd52da64..17ce42a7e387 100644 --- a/server/src/main/java/org/apache/iotdb/db/sync/sender/service/TransportHandler.java +++ b/server/src/main/java/org/apache/iotdb/db/sync/sender/service/TransportHandler.java @@ -84,10 +84,7 @@ public void start() { transportFuture = transportExecutorService.submit(transportClient); heartbeatFuture = heartbeatExecutorService.scheduleWithFixedDelay( - this::sendHeartbeat, - SyncConstant.HEARTBEAT_DELAY_SECONDS, - SyncConstant.HEARTBEAT_DELAY_SECONDS, - TimeUnit.SECONDS); + this::sendHeartbeat, 0, SyncConstant.HEARTBEAT_DELAY_SECONDS, TimeUnit.SECONDS); } public void stop() { diff --git a/server/src/test/java/org/apache/iotdb/db/sync/receiver/recovery/ReceiverLogAnalyzerTest.java b/server/src/test/java/org/apache/iotdb/db/sync/receiver/recovery/ReceiverLogAnalyzerTest.java index ae47d4d8d76e..2c15e7c2f245 100644 --- a/server/src/test/java/org/apache/iotdb/db/sync/receiver/recovery/ReceiverLogAnalyzerTest.java +++ b/server/src/test/java/org/apache/iotdb/db/sync/receiver/recovery/ReceiverLogAnalyzerTest.java @@ -77,7 +77,7 @@ public void testServiceLog() { Assert.assertEquals(1, map.get(pipe2).size()); Assert.assertEquals(1, map.get(pipe2).size()); Assert.assertEquals(PipeStatus.STOP, map.get(pipe2).get(ip2).get(2L)); - Assert.assertEquals(PipeStatus.RUNNING, map.get(pipe1).get(ip1).get(1L)); + Assert.assertEquals(PipeStatus.STOP, map.get(pipe1).get(ip1).get(1L)); Assert.assertEquals(PipeStatus.DROP, map.get(pipe1).get(ip2).get(3L)); } catch (Exception e) { Assert.fail(); From 71e9389e95718a3b7b78e2f19e9f58b3e1a39fc2 Mon Sep 17 00:00:00 2001 From: Alan Choo <43991780+HeimingZ@users.noreply.github.com> Date: Wed, 18 May 2022 10:09:53 +0800 Subject: [PATCH 037/436] [IOTDB-3196] Add wal read interface for consensus group (#5931) * add search interface for consensus group * spotless * remove --- .../consensus/wal/ConsensusReqReader.java | 86 +++++++++++++++++++ .../planner/plan/node/write/InsertNode.java | 22 +++-- .../apache/iotdb/db/wal/node/IWALNode.java | 3 +- .../apache/iotdb/db/wal/node/WALFakeNode.java | 18 ++++ .../org/apache/iotdb/db/wal/node/WALNode.java | 46 ++++++++++ 5 files changed, 168 insertions(+), 7 deletions(-) create mode 100644 consensus/src/main/java/org/apache/iotdb/consensus/wal/ConsensusReqReader.java diff --git a/consensus/src/main/java/org/apache/iotdb/consensus/wal/ConsensusReqReader.java b/consensus/src/main/java/org/apache/iotdb/consensus/wal/ConsensusReqReader.java new file mode 100644 index 000000000000..1b1929c8c8ca --- /dev/null +++ b/consensus/src/main/java/org/apache/iotdb/consensus/wal/ConsensusReqReader.java @@ -0,0 +1,86 @@ +/* + * 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.iotdb.consensus.wal; + +import org.apache.iotdb.consensus.common.request.IConsensusRequest; + +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.TimeoutException; + +/** This interface provides search interface for consensus requests via index. */ +public interface ConsensusReqReader { + /** + * Gets the consensus request at the specified position. + * + * @param index index of the consensus request to return + * @return the consensus request at the specified position + */ + IConsensusRequest getReq(long index); + + /** + * Gets the consensus requests from the specified start position. + * + * @param startIndex index of the start consensus request + * @param num number of consensus requests to return, the number of actual returned consensus + * requests may less than this value + * @return the consensus requests from the specified start position + */ + List getReqs(long startIndex, int num); + + /** + * Gets the consensus requests iterator from the specified start position. + * + * @param startIndex index of the start consensus request + * @return the consensus requests iterator from the specified start position. + */ + ReqIterator getReqIterator(long startIndex); + + /** This iterator provides blocking and non-blocking interfaces to read consensus request. */ + interface ReqIterator { + /** Like {@link Iterator#hasNext()} */ + boolean hasNext(); + + /** Like {@link Iterator#next()} */ + IConsensusRequest next(); + + /** + * Returns the next element in the iteration, blocked until next element is available. + * + * @return the next element in the iteration + */ + IConsensusRequest waitForNext() throws InterruptedException; + + /** + * Returns the next element in the iteration, blocked until next element is available or a + * specified amount of time has elapsed. + * + * @return the next element in the iteration + */ + IConsensusRequest waitForNext(long timeout) throws InterruptedException, TimeoutException; + + /** + * Skips to target position of next element in the iteration
+ * Notice: The correctness of forward skipping should be guaranteed by the caller + * + * @param targetIndex target position of next element in the iteration + */ + void skipTo(long targetIndex); + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertNode.java index 48252bbab7f0..2b79cb36a39c 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertNode.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertNode.java @@ -20,6 +20,7 @@ import org.apache.iotdb.common.rpc.thrift.TRegionReplicaSet; import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.consensus.common.request.IConsensusRequest; import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.exception.metadata.DataTypeMismatchException; import org.apache.iotdb.db.metadata.idtable.entry.IDeviceID; @@ -42,7 +43,7 @@ import java.util.Objects; import java.util.stream.Collectors; -public abstract class InsertNode extends WritePlanNode { +public abstract class InsertNode extends WritePlanNode implements IConsensusRequest { /** * if use id table, this filed is id form of device path
@@ -138,6 +139,20 @@ public void setDeviceID(IDeviceID deviceID) { this.deviceID = deviceID; } + /** + * Deserialize via {@link + * org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeType#deserialize(ByteBuffer)} + */ + @Override + public void serializeRequest(ByteBuffer buffer) { + serializeAttributes(buffer); + } + + @Override + protected void serializeAttributes(ByteBuffer byteBuffer) { + throw new NotImplementedException("serializeAttributes of InsertNode is not implemented"); + } + /** Serialized size of measurement schemas, ignoring failed time series */ protected int serializeMeasurementSchemasSize() { int byteLen = 0; @@ -268,11 +283,6 @@ public FailedMeasurementInfo( } // endregion - @Override - protected void serializeAttributes(ByteBuffer byteBuffer) { - throw new NotImplementedException("serializeAttributes of InsertNode is not implemented"); - } - @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/server/src/main/java/org/apache/iotdb/db/wal/node/IWALNode.java b/server/src/main/java/org/apache/iotdb/db/wal/node/IWALNode.java index ffaffb956114..9925015ddaef 100644 --- a/server/src/main/java/org/apache/iotdb/db/wal/node/IWALNode.java +++ b/server/src/main/java/org/apache/iotdb/db/wal/node/IWALNode.java @@ -18,6 +18,7 @@ */ package org.apache.iotdb.db.wal.node; +import org.apache.iotdb.consensus.wal.ConsensusReqReader; import org.apache.iotdb.db.engine.flush.FlushListener; import org.apache.iotdb.db.engine.memtable.IMemTable; import org.apache.iotdb.db.mpp.plan.planner.plan.node.write.InsertRowNode; @@ -28,7 +29,7 @@ import org.apache.iotdb.db.wal.utils.listener.WALFlushListener; /** This interface provides uniform interface for writing wal and making checkpoints. */ -public interface IWALNode extends FlushListener, AutoCloseable { +public interface IWALNode extends FlushListener, AutoCloseable, ConsensusReqReader { /** Log InsertRowPlan */ WALFlushListener log(int memTableId, InsertRowPlan insertRowPlan); diff --git a/server/src/main/java/org/apache/iotdb/db/wal/node/WALFakeNode.java b/server/src/main/java/org/apache/iotdb/db/wal/node/WALFakeNode.java index df3dab778e67..0ed3ab7d3d82 100644 --- a/server/src/main/java/org/apache/iotdb/db/wal/node/WALFakeNode.java +++ b/server/src/main/java/org/apache/iotdb/db/wal/node/WALFakeNode.java @@ -18,6 +18,7 @@ */ package org.apache.iotdb.db.wal.node; +import org.apache.iotdb.consensus.common.request.IConsensusRequest; import org.apache.iotdb.db.engine.memtable.IMemTable; import org.apache.iotdb.db.mpp.plan.planner.plan.node.write.InsertRowNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.write.InsertTabletNode; @@ -27,6 +28,8 @@ import org.apache.iotdb.db.wal.exception.WALException; import org.apache.iotdb.db.wal.utils.listener.WALFlushListener; +import java.util.List; + /** This class provides fake wal node when wal is disabled or exception happens. */ public class WALFakeNode implements IWALNode { private final WALFlushListener.Status status; @@ -96,6 +99,21 @@ public void onMemTableCreated(IMemTable memTable, String targetTsFile) { // do nothing } + @Override + public IConsensusRequest getReq(long index) { + throw new UnsupportedOperationException(); + } + + @Override + public List getReqs(long startIndex, int num) { + throw new UnsupportedOperationException(); + } + + @Override + public ReqIterator getReqIterator(long startIndex) { + throw new UnsupportedOperationException(); + } + @Override public void close() { // do nothing diff --git a/server/src/main/java/org/apache/iotdb/db/wal/node/WALNode.java b/server/src/main/java/org/apache/iotdb/db/wal/node/WALNode.java index 3cf5706c545b..6412658895b5 100644 --- a/server/src/main/java/org/apache/iotdb/db/wal/node/WALNode.java +++ b/server/src/main/java/org/apache/iotdb/db/wal/node/WALNode.java @@ -22,6 +22,7 @@ import org.apache.iotdb.commons.file.SystemFileFactory; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.commons.utils.TestOnly; +import org.apache.iotdb.consensus.common.request.IConsensusRequest; import org.apache.iotdb.db.conf.IoTDBConfig; import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.engine.StorageEngine; @@ -50,8 +51,10 @@ import java.io.File; import java.io.FileNotFoundException; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicLong; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -373,6 +376,49 @@ private void snapshotMemTable(DataRegion dataRegion, File tsFile, MemTableInfo m } // endregion + // region Search interfaces for consensus group + @Override + public IConsensusRequest getReq(long index) { + return null; + } + + @Override + public List getReqs(long startIndex, int num) { + return null; + } + + @Override + public ReqIterator getReqIterator(long startIndex) { + return new PlanNodeIterator(); + } + + private class PlanNodeIterator implements ReqIterator { + @Override + public boolean hasNext() { + return false; + } + + @Override + public IConsensusRequest next() { + return null; + } + + @Override + public IConsensusRequest waitForNext() throws InterruptedException { + return null; + } + + @Override + public IConsensusRequest waitForNext(long timeout) + throws InterruptedException, TimeoutException { + return null; + } + + @Override + public void skipTo(long targetIndex) {} + } + // endregion + @Override public void close() { buffer.close(); From 0a00929135fc534dd6880bc7c53867fc5d2d7a73 Mon Sep 17 00:00:00 2001 From: Haonan Date: Wed, 18 May 2022 11:20:15 +0800 Subject: [PATCH 038/436] Fix confignode script error (#5942) --- confignode/src/assembly/resources/sbin/start-confignode.bat | 2 +- confignode/src/assembly/resources/sbin/start-confignode.sh | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/confignode/src/assembly/resources/sbin/start-confignode.bat b/confignode/src/assembly/resources/sbin/start-confignode.bat index 65db4c98029f..918c0b997e5c 100644 --- a/confignode/src/assembly/resources/sbin/start-confignode.bat +++ b/confignode/src/assembly/resources/sbin/start-confignode.bat @@ -93,7 +93,7 @@ set JAVA_OPTS=-ea^ @REM ***** CLASSPATH library setting ***** @REM Ensure that any user defined CLASSPATH variables are not used on startup -if EXIST %IOTDB_HOME%\lib (set CLASSPATH="%IOTDB_HOME%\lib\*") else set CLASSPATH="%IOTDB_HOME%\..\lib\*" +if EXIST %CONFIGNODE_HOME%\lib (set CLASSPATH="%CONFIGNODE_HOME%\lib\*") else set CLASSPATH="%CONFIGNODE_HOME%\..\lib\*" set CLASSPATH=%CLASSPATH%;iotdb.ConfigNode goto okClasspath diff --git a/confignode/src/assembly/resources/sbin/start-confignode.sh b/confignode/src/assembly/resources/sbin/start-confignode.sh index fd51caa12828..8a04fb47f591 100644 --- a/confignode/src/assembly/resources/sbin/start-confignode.sh +++ b/confignode/src/assembly/resources/sbin/start-confignode.sh @@ -57,10 +57,10 @@ else echo "can't find $CONFIGNODE_CONF/confignode-env.sh" fi -if [ -d ${IOTDB_HOME}/lib ]; then -LIB_PATH=${IOTDB_HOME}/lib +if [ -d ${CONFIGNODE_HOME}/lib ]; then +LIB_PATH=${CONFIGNODE_HOME}/lib else -LIB_PATH=${IOTDB_HOME}/../lib +LIB_PATH=${CONFIGNODE_HOME}/../lib fi CLASSPATH="" From 1055a4030fbde748fba0defbc29026767ed107fa Mon Sep 17 00:00:00 2001 From: lisijia <44458757+cigarl@users.noreply.github.com> Date: Wed, 18 May 2022 11:28:41 +0800 Subject: [PATCH 039/436] [IOTDB-3200] [PartitionInfo] replace bytebuffer with stream (#5921) --- .../persistence/ClusterSchemaInfo.java | 2 +- .../confignode/persistence/NodeInfo.java | 15 ++- .../confignode/persistence/PartitionInfo.java | 109 +++++++---------- .../executor/ConfigRequestExecutor.java | 16 ++- .../persistence/PartitionInfoTest.java | 33 +++-- .../commons/partition/DataPartition.java | 113 ++++++++---------- .../commons/partition/SchemaPartition.java | 64 +++++----- .../commons/partition/DataPartitionTest.java | 24 +++- .../partition/SchemaPartitionTest.java | 23 +++- 9 files changed, 211 insertions(+), 188 deletions(-) diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/ClusterSchemaInfo.java b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/ClusterSchemaInfo.java index e9a900169836..7f5d4f0b8341 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/ClusterSchemaInfo.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/ClusterSchemaInfo.java @@ -469,7 +469,7 @@ public boolean processTakeSnapshot(File snapshotDir) throws IOException { } finally { buffer.clear(); for (int retry = 0; retry < 5; retry++) { - if (tmpFile.delete()) { + if (!tmpFile.exists() || tmpFile.delete()) { break; } else { LOGGER.warn( diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/NodeInfo.java b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/NodeInfo.java index 02765058cb1b..d6ff4a43a85e 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/NodeInfo.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/NodeInfo.java @@ -300,12 +300,23 @@ public boolean processTakeSnapshot(File snapshotDir) throws IOException, TExcept serializeDrainingDataNodes(dataOutputStream, protocol); fileOutputStream.flush(); + + fileOutputStream.close(); + + return tmpFile.renameTo(snapshotFile); + } finally { configNodeInfoReadWriteLock.readLock().unlock(); dataNodeInfoReadWriteLock.readLock().unlock(); + for (int retry = 0; retry < 5; retry++) { + if (!tmpFile.exists() || tmpFile.delete()) { + break; + } else { + LOGGER.warn( + "Can't delete temporary snapshot file: {}, retrying...", tmpFile.getAbsolutePath()); + } + } } - - return tmpFile.renameTo(snapshotFile); } private void serializeOnlineDataNode(DataOutputStream outputStream, TProtocol protocol) diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/PartitionInfo.java b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/PartitionInfo.java index 1f6d4fcdad0f..851b0d0d519c 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/PartitionInfo.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/PartitionInfo.java @@ -50,14 +50,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; @@ -91,9 +89,6 @@ public class PartitionInfo implements SnapshotProcessor { private final ReentrantReadWriteLock dataPartitionReadWriteLock; private final DataPartition dataPartition; - // The size of the buffer used for snapshot(temporary value) - private final int bufferSize = 10 * 1024 * 1024; - private final String snapshotFileName = "partition_info.bin"; public PartitionInfo() { @@ -256,7 +251,7 @@ public TSStatus createSchemaPartition(CreateSchemaPartitionReq req) { * Filter no assigned SchemaPartitionSlots * * @param partitionSlotsMap Map> - * @return Map>, SchemaPartitionSlots that is not + * @return Map>, SchemaPartitionSlots that is not * assigned in partitionSlotsMap */ public Map> filterNoAssignedSchemaPartitionSlots( @@ -339,7 +334,7 @@ public TSStatus createDataPartition(CreateDataPartitionReq req) { * * @param partitionSlotsMap Map>> - * @return Map>>, + * @return Map>>, * DataPartitionSlots that is not assigned in partitionSlotsMap */ public Map>> @@ -437,33 +432,34 @@ public boolean processTakeSnapshot(File snapshotDir) throws TException, IOExcept return false; } + // prevents temporary files from being damaged and cannot be deleted, which affects the next + // snapshot operation. File tmpFile = new File(snapshotFile.getAbsolutePath() + "-" + UUID.randomUUID()); lockAllRead(); - ByteBuffer byteBuffer = ByteBuffer.allocate(bufferSize); - try { + try (FileOutputStream fileOutputStream = new FileOutputStream(tmpFile); + DataOutputStream dataOutputStream = new DataOutputStream(fileOutputStream); + TIOStreamTransport tioStreamTransport = new TIOStreamTransport(dataOutputStream)) { + TProtocol protocol = new TBinaryProtocol(tioStreamTransport); + // serialize nextRegionGroupId - byteBuffer.putInt(nextRegionGroupId.get()); + dataOutputStream.writeInt(nextRegionGroupId.get()); // serialize regionMap - serializeRegionMap(byteBuffer); + serializeRegionMap(dataOutputStream, protocol); // serialize schemaPartition - schemaPartition.serialize(byteBuffer); + schemaPartition.serialize(dataOutputStream, protocol); // serialize dataPartition - dataPartition.serialize(byteBuffer); + dataPartition.serialize(dataOutputStream, protocol); // write to file - try (FileOutputStream fileOutputStream = new FileOutputStream(tmpFile); - FileChannel fileChannel = fileOutputStream.getChannel()) { - byteBuffer.flip(); - fileChannel.write(byteBuffer); - } + fileOutputStream.flush(); + fileOutputStream.close(); // rename file return tmpFile.renameTo(snapshotFile); } finally { unlockAllRead(); - byteBuffer.clear(); // with or without success, delete temporary files anyway for (int retry = 0; retry < 5; retry++) { - if (tmpFile.delete()) { + if (!tmpFile.exists() || tmpFile.delete()) { break; } else { LOGGER.warn( @@ -486,22 +482,19 @@ public void processLoadSnapshot(File snapshotDir) throws TException, IOException // no operations are processed at this time lockAllWrite(); - ByteBuffer buffer = ByteBuffer.allocate(bufferSize); try (FileInputStream fileInputStream = new FileInputStream(snapshotFile); - FileChannel fileChannel = fileInputStream.getChannel()) { - // get buffer from fileChannel - fileChannel.read(buffer); - buffer.flip(); + DataInputStream dataInputStream = new DataInputStream(fileInputStream); + TIOStreamTransport tioStreamTransport = new TIOStreamTransport(dataInputStream)) { + TProtocol protocol = new TBinaryProtocol(tioStreamTransport); // before restoring a snapshot, clear all old data clear(); // start to restore - nextRegionGroupId.set(buffer.getInt()); - deserializeRegionMap(buffer); - schemaPartition.deserialize(buffer); - dataPartition.deserialize(buffer); + nextRegionGroupId.set(dataInputStream.readInt()); + deserializeRegionMap(dataInputStream, protocol); + schemaPartition.deserialize(dataInputStream, protocol); + dataPartition.deserialize(dataInputStream, protocol); } finally { unlockAllWrite(); - buffer.clear(); } } @@ -544,41 +537,29 @@ public Map getRegionSlotsCounter() { return regionSlotsCounter; } - private void serializeRegionMap(ByteBuffer buffer) throws TException, IOException { - try (ByteArrayOutputStream out = new ByteArrayOutputStream(); - TIOStreamTransport tioStreamTransport = new TIOStreamTransport(out)) { - TProtocol protocol = new TBinaryProtocol(tioStreamTransport); - - for (TConsensusGroupId consensusGroupId : regionReplicaMap.keySet()) { - consensusGroupId.write(protocol); - regionReplicaMap.get(consensusGroupId).write(protocol); - protocol.writeI64(regionSlotsCounter.get(consensusGroupId)); - } - - byte[] toArray = out.toByteArray(); - buffer.putInt(toArray.length); - buffer.put(toArray); + private void serializeRegionMap(DataOutputStream dataOutputStream, TProtocol protocol) + throws TException, IOException { + dataOutputStream.writeInt(regionReplicaMap.size()); + for (TConsensusGroupId consensusGroupId : regionReplicaMap.keySet()) { + consensusGroupId.write(protocol); + regionReplicaMap.get(consensusGroupId).write(protocol); + protocol.writeI64(regionSlotsCounter.get(consensusGroupId)); } } - private void deserializeRegionMap(ByteBuffer buffer) throws TException, IOException { - int length = buffer.getInt(); - byte[] regionMapBuffer = new byte[length]; - buffer.get(regionMapBuffer); - try (ByteArrayInputStream in = new ByteArrayInputStream(regionMapBuffer); - TIOStreamTransport tioStreamTransport = new TIOStreamTransport(in)) { - while (in.available() > 0) { - TProtocol protocol = new TBinaryProtocol(tioStreamTransport); - - TConsensusGroupId tConsensusGroupId = new TConsensusGroupId(); - tConsensusGroupId.read(protocol); - TRegionReplicaSet tRegionReplicaSet = new TRegionReplicaSet(); - tRegionReplicaSet.read(protocol); - Long count = protocol.readI64(); - - regionReplicaMap.put(tConsensusGroupId, tRegionReplicaSet); - regionSlotsCounter.put(tConsensusGroupId, count); - } + private void deserializeRegionMap(DataInputStream dataInputStream, TProtocol protocol) + throws TException, IOException { + int size = dataInputStream.readInt(); + while (size > 0) { + TConsensusGroupId tConsensusGroupId = new TConsensusGroupId(); + tConsensusGroupId.read(protocol); + TRegionReplicaSet tRegionReplicaSet = new TRegionReplicaSet(); + tRegionReplicaSet.read(protocol); + Long count = protocol.readI64(); + + regionReplicaMap.put(tConsensusGroupId, tRegionReplicaSet); + regionSlotsCounter.put(tConsensusGroupId, count); + size--; } } diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigRequestExecutor.java b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigRequestExecutor.java index 66b3872e12d0..4b7bc5d7ced6 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigRequestExecutor.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigRequestExecutor.java @@ -175,11 +175,21 @@ public TSStatus executorNonQueryPlan(ConfigRequest req) public boolean takeSnapshot(File snapshotDir) { - if (!snapshotDir.exists() && !snapshotDir.mkdirs()) { - LOGGER.error("snapshot directory [{}] can not be created.", snapshotDir.getAbsolutePath()); - return false; + // consensus layer needs to ensure that the directory exists. + // if it does not exist, print a log to warn there may have a problem. + if (!snapshotDir.exists()) { + LOGGER.warn( + "snapshot directory [{}] is not exist,start to create it.", + snapshotDir.getAbsolutePath()); + // try to create a directory to enable snapshot operation + if (!snapshotDir.mkdirs()) { + LOGGER.error("snapshot directory [{}] can not be created.", snapshotDir.getAbsolutePath()); + return false; + } } + // If the directory is not empty, we should not continue the snapshot operation, + // which may result in incorrect results. File[] fileList = snapshotDir.listFiles(); if (fileList != null && fileList.length > 0) { LOGGER.error("snapshot directory [{}] is not empty.", snapshotDir.getAbsolutePath()); diff --git a/confignode/src/test/java/org/apache/iotdb/confignode/persistence/PartitionInfoTest.java b/confignode/src/test/java/org/apache/iotdb/confignode/persistence/PartitionInfoTest.java index 820ec3c1e7cf..58bfb4c8d8b4 100644 --- a/confignode/src/test/java/org/apache/iotdb/confignode/persistence/PartitionInfoTest.java +++ b/confignode/src/test/java/org/apache/iotdb/confignode/persistence/PartitionInfoTest.java @@ -123,8 +123,12 @@ public void testSnapshot() throws TException, IOException { generateTConsensusGroupId( testFlag.DataPartition.getFlag(), TConsensusGroupType.DataRegion)); partitionInfo.createDataPartition(createDataPartitionReq); + + Map>>> + dataMap_before = partitionInfo.getDataPartition().getDataPartitionMap(); int nextId = partitionInfo.getNextRegionGroupId(); + Map counter_before = partitionInfo.getRegionSlotsCounter(); partitionInfo.processTakeSnapshot(snapshotDir); partitionInfo.clear(); partitionInfo.processLoadSnapshot(snapshotDir); @@ -149,18 +153,14 @@ public void testSnapshot() throws TException, IOException { Assert.assertEquals(1, reloadTRegionReplicaSet.size()); Assert.assertEquals(dataRegionReplicaSet, reloadTRegionReplicaSet.get(0)); - Assert.assertEquals( - createDataPartitionReq.getAssignedDataPartition(), - partitionInfo.getDataPartition().getDataPartitionMap()); - Assert.assertEquals( createSchemaPartitionReq.getAssignedSchemaPartition(), partitionInfo.getSchemaPartition().getSchemaPartitionMap()); Assert.assertEquals(2, partitionInfo.getRegionSlotsCounter().size()); - for (Long count : partitionInfo.getRegionSlotsCounter().values()) { - Assert.assertEquals(1, count.intValue()); - } + Assert.assertEquals(counter_before, partitionInfo.getRegionSlotsCounter()); + + Assert.assertEquals(dataMap_before, partitionInfo.getDataPartition().getDataPartitionMap()); } private TRegionReplicaSet generateTRegionReplicaSet( @@ -199,19 +199,30 @@ private CreateSchemaPartitionReq generateCreateSchemaPartitionReq( private CreateDataPartitionReq generateCreateDataPartitionReq( int startFlag, TConsensusGroupId tConsensusGroupId) { + startFlag = startFlag / 10; CreateDataPartitionReq createSchemaPartitionReq = new CreateDataPartitionReq(); // Map>>> Map>>> dataPartitionMap = new HashMap<>(); Map> relationInfo = new HashMap<>(); - relationInfo.put( - new TTimePartitionSlot(System.currentTimeMillis() / 1000), - Collections.singletonList(generateTRegionReplicaSet(startFlag, tConsensusGroupId))); + + List tRegionReplicaSets = new ArrayList<>(); + + for (int i = 0; i <= startFlag; i++) { + for (int j = 0; j <= startFlag; j++) { + tRegionReplicaSets.add(generateTRegionReplicaSet(j + startFlag, tConsensusGroupId)); + } + relationInfo.put( + new TTimePartitionSlot((System.currentTimeMillis() / 1000) + i), tRegionReplicaSets); + } Map>> slotInfo = new HashMap<>(); - slotInfo.put(new TSeriesPartitionSlot(startFlag), relationInfo); + + for (int i = 0; i <= startFlag; i++) { + slotInfo.put(new TSeriesPartitionSlot(startFlag + i), relationInfo); + } dataPartitionMap.put("root.test.data.sg", slotInfo); createSchemaPartitionReq.setAssignedDataPartition(dataPartitionMap); diff --git a/node-commons/src/main/java/org/apache/iotdb/commons/partition/DataPartition.java b/node-commons/src/main/java/org/apache/iotdb/commons/partition/DataPartition.java index d66ae081138f..5ed7fe3e50ea 100644 --- a/node-commons/src/main/java/org/apache/iotdb/commons/partition/DataPartition.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/partition/DataPartition.java @@ -24,14 +24,11 @@ import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils; import org.apache.thrift.TException; -import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.thrift.protocol.TProtocol; -import org.apache.thrift.transport.TIOStreamTransport; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; import java.io.IOException; -import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -248,75 +245,69 @@ public void createDataPartition( .put(timePartitionSlot, Collections.singletonList(regionReplicaSet)); } - public void serialize(ByteBuffer buffer) throws IOException, TException { - try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) { - for (Entry< - String, Map>>> - entry : dataPartitionMap.entrySet()) { - ReadWriteIOUtils.write(entry.getKey(), byteArrayOutputStream); - try (TIOStreamTransport tioStreamTransport = - new TIOStreamTransport(byteArrayOutputStream)) { - TProtocol protocol = new TBinaryProtocol(tioStreamTransport); - for (Entry>> - seriesPartitionSlotMapEntry : entry.getValue().entrySet()) { - seriesPartitionSlotMapEntry.getKey().write(protocol); - for (Entry> timePartitionSlotListEntry : - seriesPartitionSlotMapEntry.getValue().entrySet()) { - timePartitionSlotListEntry.getKey().write(protocol); - ReadWriteIOUtils.write( - timePartitionSlotListEntry.getValue().size(), byteArrayOutputStream); - timePartitionSlotListEntry - .getValue() - .forEach( - x -> { - try { - x.write(protocol); - } catch (TException e) { - throw new RuntimeException(e); - } - }); - } + public void serialize(DataOutputStream dataOutputStream, TProtocol protocol) + throws IOException, TException { + // Map>>> + dataOutputStream.writeInt(dataPartitionMap.size()); + for (Entry>>> + entry : dataPartitionMap.entrySet()) { + ReadWriteIOUtils.write(entry.getKey(), dataOutputStream); + ReadWriteIOUtils.write(entry.getValue().size(), dataOutputStream); + for (Entry>> + seriesPartitionSlotMapEntry : entry.getValue().entrySet()) { + seriesPartitionSlotMapEntry.getKey().write(protocol); + ReadWriteIOUtils.write(seriesPartitionSlotMapEntry.getValue().size(), dataOutputStream); + for (Entry> timePartitionSlotListEntry : + seriesPartitionSlotMapEntry.getValue().entrySet()) { + timePartitionSlotListEntry.getKey().write(protocol); + ReadWriteIOUtils.write(timePartitionSlotListEntry.getValue().size(), dataOutputStream); + for (TRegionReplicaSet tRegionReplicaSet : timePartitionSlotListEntry.getValue()) { + tRegionReplicaSet.write(protocol); } } } - byte[] toArray = byteArrayOutputStream.toByteArray(); - buffer.putInt(toArray.length); - buffer.put(toArray); } } - public void deserialize(ByteBuffer buffer) throws TException, IOException { - int length = buffer.getInt(); - byte[] bytes = new byte[length]; - buffer.get(bytes); - - try (ByteArrayInputStream in = new ByteArrayInputStream(bytes); - TIOStreamTransport tioStreamTransport = new TIOStreamTransport(in)) { + public void deserialize(DataInputStream dataInputStream, TProtocol protocol) + throws TException, IOException { + int storageGroupNum = dataInputStream.readInt(); + // Map>>> + while (storageGroupNum > 0) { + String storageGroup = ReadWriteIOUtils.readString(dataInputStream); + int tSeriesPartitionSlotNum = ReadWriteIOUtils.readInt(dataInputStream); - TProtocol protocol = new TBinaryProtocol(tioStreamTransport); - // Map>>> - while (in.available() > 0) { - String storageGroup = ReadWriteIOUtils.readString(in); + Map>> + seriesPartitionSlotMapHashMap = new HashMap<>(); + while (tSeriesPartitionSlotNum > 0) { TSeriesPartitionSlot tSeriesPartitionSlot = new TSeriesPartitionSlot(); tSeriesPartitionSlot.read(protocol); - TTimePartitionSlot tTimePartitionSlot = new TTimePartitionSlot(); - tTimePartitionSlot.read(protocol); - int size = ReadWriteIOUtils.readInt(in); - List tRegionMessageList = new ArrayList<>(); - while (size > 0) { - TRegionReplicaSet tRegionReplicaSet = new TRegionReplicaSet(); - tRegionReplicaSet.read(protocol); - tRegionMessageList.add(tRegionReplicaSet); - size--; - } + + int tTimePartitionSlotNum = ReadWriteIOUtils.readInt(dataInputStream); + Map> timePartitionSlotListHashMap = new HashMap<>(); - timePartitionSlotListHashMap.put(tTimePartitionSlot, tRegionMessageList); - Map>> - seriesPartitionSlotMapHashMap = new HashMap<>(); + while (tTimePartitionSlotNum > 0) { + TTimePartitionSlot tTimePartitionSlot = new TTimePartitionSlot(); + tTimePartitionSlot.read(protocol); + int size = ReadWriteIOUtils.readInt(dataInputStream); + + List tRegionMessageList = new ArrayList<>(); + while (size > 0) { + TRegionReplicaSet tRegionReplicaSet = new TRegionReplicaSet(); + tRegionReplicaSet.read(protocol); + tRegionMessageList.add(tRegionReplicaSet); + size--; + } + timePartitionSlotListHashMap.put(tTimePartitionSlot, tRegionMessageList); + tTimePartitionSlotNum--; + } + seriesPartitionSlotMapHashMap.put(tSeriesPartitionSlot, timePartitionSlotListHashMap); - dataPartitionMap.put(storageGroup, seriesPartitionSlotMapHashMap); + tSeriesPartitionSlotNum--; } + dataPartitionMap.put(storageGroup, seriesPartitionSlotMapHashMap); + storageGroupNum--; } } } diff --git a/node-commons/src/main/java/org/apache/iotdb/commons/partition/SchemaPartition.java b/node-commons/src/main/java/org/apache/iotdb/commons/partition/SchemaPartition.java index 165ae8b9b6b4..18d1c2a39aa3 100644 --- a/node-commons/src/main/java/org/apache/iotdb/commons/partition/SchemaPartition.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/partition/SchemaPartition.java @@ -23,15 +23,11 @@ import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils; import org.apache.thrift.TException; -import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.thrift.protocol.TProtocol; -import org.apache.thrift.transport.TByteBuffer; -import org.apache.thrift.transport.TIOStreamTransport; -import org.apache.thrift.transport.TTransport; -import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; import java.io.IOException; -import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -174,55 +170,51 @@ public void createSchemaPartition( .put(seriesPartitionSlot, regionReplicaSet); } - public void serialize(ByteBuffer buffer) throws IOException, TException { - try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) { - for (Entry> entry : - schemaPartitionMap.entrySet()) { - ReadWriteIOUtils.write(entry.getKey(), byteArrayOutputStream); - writeMap(entry.getValue(), byteArrayOutputStream); - } - byte[] toArray = byteArrayOutputStream.toByteArray(); - buffer.putInt(toArray.length); - buffer.put(toArray); + public void serialize(DataOutputStream dataOutputStream, TProtocol protocol) + throws IOException, TException { + dataOutputStream.writeInt(schemaPartitionMap.size()); + for (Entry> entry : + schemaPartitionMap.entrySet()) { + ReadWriteIOUtils.write(entry.getKey(), dataOutputStream); + writeMap(entry.getValue(), dataOutputStream, protocol); } } - public void deserialize(ByteBuffer buffer) throws TException, IOException { - int length = buffer.getInt(); - byte[] result = new byte[length]; - buffer.get(result); - ByteBuffer byteBuffer = ByteBuffer.wrap(result); - while (byteBuffer.hasRemaining()) { - String key = ReadWriteIOUtils.readString(byteBuffer); - Map value = readMap(byteBuffer); + public void deserialize(DataInputStream dataInputStream, TProtocol protocol) + throws TException, IOException { + int size = dataInputStream.readInt(); + while (size > 0) { + String key = ReadWriteIOUtils.readString(dataInputStream); + Map value = readMap(dataInputStream, protocol); schemaPartitionMap.put(key, value); + size--; } } - private Map readMap(ByteBuffer buffer) - throws TException { + private Map readMap( + DataInputStream dataInputStream, TProtocol protocol) throws TException, IOException { + int size = dataInputStream.readInt(); Map result = new HashMap<>(); - try (TTransport transport = new TByteBuffer(buffer)) { - TProtocol protocol = new TBinaryProtocol(transport); + while (size > 0) { TSeriesPartitionSlot tSeriesPartitionSlot = new TSeriesPartitionSlot(); tSeriesPartitionSlot.read(protocol); TRegionReplicaSet tRegionReplicaSet = new TRegionReplicaSet(); tRegionReplicaSet.read(protocol); result.put(tSeriesPartitionSlot, tRegionReplicaSet); + size--; } return result; } private void writeMap( Map valueMap, - ByteArrayOutputStream byteArrayOutputStream) - throws TException { - try (TIOStreamTransport tioStreamTransport = new TIOStreamTransport(byteArrayOutputStream)) { - TProtocol protocol = new TBinaryProtocol(tioStreamTransport); - for (Entry entry : valueMap.entrySet()) { - entry.getKey().write(protocol); - entry.getValue().write(protocol); - } + DataOutputStream dataOutputStream, + TProtocol protocol) + throws TException, IOException { + dataOutputStream.writeInt(valueMap.size()); + for (Entry entry : valueMap.entrySet()) { + entry.getKey().write(protocol); + entry.getValue().write(protocol); } } } diff --git a/node-commons/src/test/java/org/apache/iotdb/commons/partition/DataPartitionTest.java b/node-commons/src/test/java/org/apache/iotdb/commons/partition/DataPartitionTest.java index 769b365e292d..ca9f40d92ef4 100644 --- a/node-commons/src/test/java/org/apache/iotdb/commons/partition/DataPartitionTest.java +++ b/node-commons/src/test/java/org/apache/iotdb/commons/partition/DataPartitionTest.java @@ -24,11 +24,17 @@ import org.apache.iotdb.common.rpc.thrift.TTimePartitionSlot; import org.apache.thrift.TException; +import org.apache.thrift.protocol.TBinaryProtocol; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.transport.TIOStreamTransport; import org.junit.Assert; import org.junit.Test; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; import java.io.IOException; -import java.nio.ByteBuffer; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -47,13 +53,21 @@ public void testSerialize() throws TException, IOException { generateCreateDataPartitionMap( dataPartitionFlag, generateTConsensusGroupId(dataPartitionFlag)); dataPartition.setDataPartitionMap(assignedDataPartition); - ByteBuffer buffer = ByteBuffer.allocate(10 * 1024); - dataPartition.serialize(buffer); + + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream); + TIOStreamTransport tioStreamTransport = new TIOStreamTransport(dataOutputStream); + TProtocol protocol = new TBinaryProtocol(tioStreamTransport); + dataPartition.serialize(dataOutputStream, protocol); DataPartition newDataPartition = new DataPartition(new HashMap<>(), seriesPartitionExecutorClass, 1); - buffer.flip(); - newDataPartition.deserialize(buffer); + ByteArrayInputStream byteArrayInputStream = + new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); + DataInputStream dataInputStream = new DataInputStream(byteArrayInputStream); + tioStreamTransport = new TIOStreamTransport(dataInputStream); + protocol = new TBinaryProtocol(tioStreamTransport); + newDataPartition.deserialize(dataInputStream, protocol); Assert.assertEquals(assignedDataPartition, newDataPartition.getDataPartitionMap()); } } diff --git a/node-commons/src/test/java/org/apache/iotdb/commons/partition/SchemaPartitionTest.java b/node-commons/src/test/java/org/apache/iotdb/commons/partition/SchemaPartitionTest.java index 3d052a3eafb4..1d1d7c3f1c56 100644 --- a/node-commons/src/test/java/org/apache/iotdb/commons/partition/SchemaPartitionTest.java +++ b/node-commons/src/test/java/org/apache/iotdb/commons/partition/SchemaPartitionTest.java @@ -23,11 +23,17 @@ import org.apache.iotdb.common.rpc.thrift.TSeriesPartitionSlot; import org.apache.thrift.TException; +import org.apache.thrift.protocol.TBinaryProtocol; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.transport.TIOStreamTransport; import org.junit.Assert; import org.junit.Test; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; import java.io.IOException; -import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Map; @@ -44,13 +50,20 @@ public void testSerialize() throws TException, IOException { generateCreateSchemaPartitionMap( schemaPartitionFlag, generateTConsensusGroupId(schemaPartitionFlag)); schemaPartition.setSchemaPartitionMap(schemaPartitionMap); - ByteBuffer buffer = ByteBuffer.allocate(10 * 1024); - schemaPartition.serialize(buffer); + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream); + TIOStreamTransport tioStreamTransport = new TIOStreamTransport(dataOutputStream); + TProtocol protocol = new TBinaryProtocol(tioStreamTransport); + schemaPartition.serialize(dataOutputStream, protocol); SchemaPartition newSchemaPartition = new SchemaPartition(new HashMap<>(), seriesPartitionExecutorClass, 1); - buffer.flip(); - newSchemaPartition.deserialize(buffer); + ByteArrayInputStream byteArrayInputStream = + new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); + DataInputStream dataInputStream = new DataInputStream(byteArrayInputStream); + tioStreamTransport = new TIOStreamTransport(dataInputStream); + protocol = new TBinaryProtocol(tioStreamTransport); + newSchemaPartition.deserialize(dataInputStream, protocol); Assert.assertEquals(schemaPartitionMap, newSchemaPartition.getSchemaPartitionMap()); } } From 80475f77d0a0cd111591cd064e11b3e40d818415 Mon Sep 17 00:00:00 2001 From: Liu Xuxin <37140360+THUMarkLau@users.noreply.github.com> Date: Wed, 18 May 2022 17:03:15 +0800 Subject: [PATCH 040/436] [IOTDB-3213] Apply visitor pattern for DataRegionStateMachine (#5944) --- .../statemachine/DataRegionStateMachine.java | 40 +----- .../visitor/DataExecutionVisitor.java | 116 ++++++++++++++++++ .../plan/planner/plan/node/PlanVisitor.java | 25 ++++ 3 files changed, 143 insertions(+), 38 deletions(-) create mode 100644 server/src/main/java/org/apache/iotdb/db/consensus/statemachine/visitor/DataExecutionVisitor.java diff --git a/server/src/main/java/org/apache/iotdb/db/consensus/statemachine/DataRegionStateMachine.java b/server/src/main/java/org/apache/iotdb/db/consensus/statemachine/DataRegionStateMachine.java index 7fe44559f73e..89110c470095 100644 --- a/server/src/main/java/org/apache/iotdb/db/consensus/statemachine/DataRegionStateMachine.java +++ b/server/src/main/java/org/apache/iotdb/db/consensus/statemachine/DataRegionStateMachine.java @@ -20,28 +20,17 @@ package org.apache.iotdb.db.consensus.statemachine; import org.apache.iotdb.common.rpc.thrift.TSStatus; -import org.apache.iotdb.commons.consensus.DataRegionId; -import org.apache.iotdb.commons.utils.StatusUtils; import org.apache.iotdb.consensus.common.DataSet; -import org.apache.iotdb.db.engine.StorageEngineV2; +import org.apache.iotdb.db.consensus.statemachine.visitor.DataExecutionVisitor; import org.apache.iotdb.db.engine.storagegroup.DataRegion; -import org.apache.iotdb.db.exception.BatchProcessException; import org.apache.iotdb.db.mpp.execution.fragment.FragmentInstanceManager; import org.apache.iotdb.db.mpp.plan.planner.plan.FragmentInstance; -import org.apache.iotdb.db.mpp.plan.planner.plan.node.DeleteRegionNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNode; -import org.apache.iotdb.db.mpp.plan.planner.plan.node.write.InsertMultiTabletsNode; -import org.apache.iotdb.db.mpp.plan.planner.plan.node.write.InsertRowNode; -import org.apache.iotdb.db.mpp.plan.planner.plan.node.write.InsertRowsNode; -import org.apache.iotdb.db.mpp.plan.planner.plan.node.write.InsertRowsOfOneDeviceNode; -import org.apache.iotdb.db.mpp.plan.planner.plan.node.write.InsertTabletNode; -import org.apache.iotdb.rpc.RpcUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; -import java.util.Arrays; public class DataRegionStateMachine extends BaseStateMachine { @@ -73,32 +62,7 @@ public void loadSnapshot(File latestSnapshotRootDir) {} @Override protected TSStatus write(FragmentInstance fragmentInstance) { PlanNode planNode = fragmentInstance.getFragment().getRoot(); - try { - if (planNode instanceof InsertRowNode) { - region.insert((InsertRowNode) planNode); - } else if (planNode instanceof InsertTabletNode) { - region.insertTablet((InsertTabletNode) planNode); - } else if (planNode instanceof InsertRowsNode) { - region.insert((InsertRowsNode) planNode); - } else if (planNode instanceof InsertMultiTabletsNode) { - region.insertTablets((InsertMultiTabletsNode) (planNode)); - } else if (planNode instanceof InsertRowsOfOneDeviceNode) { - region.insert((InsertRowsOfOneDeviceNode) planNode); - } else if (planNode instanceof DeleteRegionNode) { - region.syncDeleteDataFiles(); - StorageEngineV2.getInstance() - .deleteDataRegion((DataRegionId) ((DeleteRegionNode) planNode).getConsensusGroupId()); - } else { - logger.error("Unsupported plan node for writing to data region : {}", planNode); - return StatusUtils.UNSUPPORTED_OPERATION; - } - } catch (BatchProcessException e) { - return RpcUtils.getStatus(Arrays.asList(e.getFailingStatus())); - } catch (Exception e) { - logger.error("Error in executing plan node: {}", planNode); - return StatusUtils.EXECUTE_STATEMENT_ERROR; - } - return StatusUtils.OK; + return planNode.accept(new DataExecutionVisitor(), region); } @Override diff --git a/server/src/main/java/org/apache/iotdb/db/consensus/statemachine/visitor/DataExecutionVisitor.java b/server/src/main/java/org/apache/iotdb/db/consensus/statemachine/visitor/DataExecutionVisitor.java new file mode 100644 index 000000000000..05a303c40f30 --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/consensus/statemachine/visitor/DataExecutionVisitor.java @@ -0,0 +1,116 @@ +/* + * 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.iotdb.db.consensus.statemachine.visitor; + +import org.apache.iotdb.common.rpc.thrift.TSStatus; +import org.apache.iotdb.commons.consensus.DataRegionId; +import org.apache.iotdb.commons.utils.StatusUtils; +import org.apache.iotdb.db.engine.StorageEngineV2; +import org.apache.iotdb.db.engine.storagegroup.DataRegion; +import org.apache.iotdb.db.exception.BatchProcessException; +import org.apache.iotdb.db.exception.TriggerExecutionException; +import org.apache.iotdb.db.exception.WriteProcessException; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.DeleteRegionNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanVisitor; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.write.InsertMultiTabletsNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.write.InsertRowNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.write.InsertRowsNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.write.InsertRowsOfOneDeviceNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.write.InsertTabletNode; +import org.apache.iotdb.rpc.RpcUtils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Arrays; + +public class DataExecutionVisitor extends PlanVisitor { + private static final Logger LOGGER = LoggerFactory.getLogger(DataExecutionVisitor.class); + + @Override + public TSStatus visitPlan(PlanNode node, DataRegion context) { + return null; + } + + @Override + public TSStatus visitInsertRow(InsertRowNode node, DataRegion dataRegion) { + try { + dataRegion.insert(node); + return StatusUtils.OK; + } catch (WriteProcessException | TriggerExecutionException e) { + LOGGER.error("Error in executing plan node: {}", node, e); + return StatusUtils.EXECUTE_STATEMENT_ERROR; + } + } + + @Override + public TSStatus visitInsertTablet(InsertTabletNode node, DataRegion dataRegion) { + try { + dataRegion.insertTablet(node); + return StatusUtils.OK; + } catch (TriggerExecutionException e) { + LOGGER.error("Error in executing plan node: {}", node, e); + return StatusUtils.EXECUTE_STATEMENT_ERROR; + } catch (BatchProcessException e) { + return RpcUtils.getStatus(Arrays.asList(e.getFailingStatus())); + } + } + + @Override + public TSStatus visitInsertRows(InsertRowsNode node, DataRegion dataRegion) { + try { + dataRegion.insert(node); + return StatusUtils.OK; + } catch (BatchProcessException e) { + return RpcUtils.getStatus(Arrays.asList(e.getFailingStatus())); + } + } + + @Override + public TSStatus visitInsertMultiTablets(InsertMultiTabletsNode node, DataRegion dataRegion) { + try { + dataRegion.insertTablets(node); + return StatusUtils.OK; + } catch (BatchProcessException e) { + return RpcUtils.getStatus(Arrays.asList(e.getFailingStatus())); + } + } + + @Override + public TSStatus visitInsertRowsOfOneDevice( + InsertRowsOfOneDeviceNode node, DataRegion dataRegion) { + try { + dataRegion.insert(node); + return StatusUtils.OK; + } catch (WriteProcessException | TriggerExecutionException e) { + LOGGER.error("Error in executing plan node: {}", node, e); + return StatusUtils.EXECUTE_STATEMENT_ERROR; + } catch (BatchProcessException e) { + return RpcUtils.getStatus(Arrays.asList(e.getFailingStatus())); + } + } + + @Override + public TSStatus visitDeleteRegion(DeleteRegionNode node, DataRegion dataRegion) { + dataRegion.syncDeleteDataFiles(); + StorageEngineV2.getInstance().deleteDataRegion((DataRegionId) node.getConsensusGroupId()); + return StatusUtils.OK; + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/PlanVisitor.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/PlanVisitor.java index 61470a7f2026..4d552df27b23 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/PlanVisitor.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/PlanVisitor.java @@ -52,6 +52,11 @@ import org.apache.iotdb.db.mpp.plan.planner.plan.node.source.AlignedSeriesScanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.source.SeriesAggregationScanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.source.SeriesScanNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.write.InsertMultiTabletsNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.write.InsertRowNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.write.InsertRowsNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.write.InsertRowsOfOneDeviceNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.write.InsertTabletNode; public abstract class PlanVisitor { @@ -200,4 +205,24 @@ public R visitTransform(TransformNode node, C context) { public R visitDeleteRegion(DeleteRegionNode node, C context) { return visitPlan(node, context); } + + public R visitInsertRow(InsertRowNode node, C context) { + return visitPlan(node, context); + } + + public R visitInsertTablet(InsertTabletNode node, C context) { + return visitPlan(node, context); + } + + public R visitInsertRows(InsertRowsNode node, C context) { + return visitPlan(node, context); + } + + public R visitInsertMultiTablets(InsertMultiTabletsNode node, C context) { + return visitPlan(node, context); + } + + public R visitInsertRowsOfOneDevice(InsertRowsOfOneDeviceNode node, C context) { + return visitPlan(node, context); + } } From 05fc9f5421a47b589d4bec87b8655a7e3515beaa Mon Sep 17 00:00:00 2001 From: Marcos_Zyk <38524330+MarcosZyk@users.noreply.github.com> Date: Wed, 18 May 2022 17:04:23 +0800 Subject: [PATCH 041/436] [IOTDB-3024] Implement SchemaRegion Memory mode snapshot (#5925) --- .../schemaregion/rocksdb/RSchemaRegion.java | 20 +++- .../SchemaRegionStateMachine.java | 6 +- .../iotdb/db/metadata/MetadataConstant.java | 5 + .../iotdb/db/metadata/logfile/MLogWriter.java | 9 ++ .../metadata/schemaregion/ISchemaRegion.java | 8 +- .../schemaregion/SchemaRegionMemoryImpl.java | 81 +++++++++++++- .../SchemaRegionSchemaFileImpl.java | 22 +++- .../iotdb/db/metadata/tag/TagLogFile.java | 12 ++- .../iotdb/db/metadata/tag/TagManager.java | 34 ++++++ .../schemaRegion/SchemaRegionTest.java | 100 ++++++++++++++++++ 10 files changed, 283 insertions(+), 14 deletions(-) create mode 100644 server/src/test/java/org/apache/iotdb/db/metadata/schemaRegion/SchemaRegionTest.java diff --git a/schema-engine-rocksdb/src/main/java/org/apache/iotdb/db/metadata/schemaregion/rocksdb/RSchemaRegion.java b/schema-engine-rocksdb/src/main/java/org/apache/iotdb/db/metadata/schemaregion/rocksdb/RSchemaRegion.java index 25156fad17d6..da467999ae75 100644 --- a/schema-engine-rocksdb/src/main/java/org/apache/iotdb/db/metadata/schemaregion/rocksdb/RSchemaRegion.java +++ b/schema-engine-rocksdb/src/main/java/org/apache/iotdb/db/metadata/schemaregion/rocksdb/RSchemaRegion.java @@ -165,7 +165,8 @@ public RSchemaRegion( throws MetadataException { this.schemaRegionId = schemaRegionId; storageGroupFullPath = storageGroup.getFullPath(); - init(storageGroupMNode); + this.storageGroupMNode = storageGroupMNode; + init(); try { readWriteHandler = new RSchemaReadWriteHandler(schemaRegionDirPath, rSchemaConfLoader); } catch (RocksDBException e) { @@ -175,8 +176,7 @@ public RSchemaRegion( } @Override - public void init(IStorageGroupMNode storageGroupMNode) throws MetadataException { - this.storageGroupMNode = storageGroupMNode; + public void init() throws MetadataException { schemaRegionDirPath = config.getSchemaDir() + File.separator @@ -218,6 +218,20 @@ public void deleteSchemaRegion() throws MetadataException { SchemaRegionUtils.deleteSchemaRegionFolder(schemaRegionDirPath, logger); } + @Override + public boolean createSnapshot(File snapshotDir) { + // todo implement this + throw new UnsupportedOperationException( + "Rocksdb mode currently doesn't support snapshot feature."); + } + + @Override + public void loadSnapshot(File latestSnapshotRootDir) { + // todo implement this + throw new UnsupportedOperationException( + "Rocksdb mode currently doesn't support snapshot feature."); + } + @Override public void createTimeseries(CreateTimeSeriesPlan plan, long offset) throws MetadataException { try { diff --git a/server/src/main/java/org/apache/iotdb/db/consensus/statemachine/SchemaRegionStateMachine.java b/server/src/main/java/org/apache/iotdb/db/consensus/statemachine/SchemaRegionStateMachine.java index 888346907a68..37e7f7f5916f 100644 --- a/server/src/main/java/org/apache/iotdb/db/consensus/statemachine/SchemaRegionStateMachine.java +++ b/server/src/main/java/org/apache/iotdb/db/consensus/statemachine/SchemaRegionStateMachine.java @@ -52,11 +52,13 @@ public void stop() {} @Override public boolean takeSnapshot(File snapshotDir) { - return false; + return schemaRegion.createSnapshot(snapshotDir); } @Override - public void loadSnapshot(File latestSnapshotRootDir) {} + public void loadSnapshot(File latestSnapshotRootDir) { + schemaRegion.loadSnapshot(latestSnapshotRootDir); + } @Override protected TSStatus write(FragmentInstance fragmentInstance) { diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/MetadataConstant.java b/server/src/main/java/org/apache/iotdb/db/metadata/MetadataConstant.java index 8dbb77023241..ffc2e58dd15f 100644 --- a/server/src/main/java/org/apache/iotdb/db/metadata/MetadataConstant.java +++ b/server/src/main/java/org/apache/iotdb/db/metadata/MetadataConstant.java @@ -45,6 +45,11 @@ private MetadataConstant() { public static final String SCHEMA_FILE_NAME = "schema_file.pst"; public static final String SCHEMA_LOG_FILE_NAME = "schema_file_log.bin"; + public static final String METADATA_LOG_SNAPSHOT = "mlog.bin.snapshot"; + public static final String METADATA_LOG_SNAPSHOT_TMP = "mlog.bin.snapshot.tmp"; + public static final String TAG_LOG_SNAPSHOT = "tlog.txt.snapshot"; + public static final String TAG_LOG_SNAPSHOT_TMP = "tlog.txt.snapshot.tmp"; + public static final String[] ALL_RESULT_NODES = new String[] {"root", "**"}; public static final PartialPath ALL_MATCH_PATTERN = new PartialPath(new String[] {"root", "**"}); diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/logfile/MLogWriter.java b/server/src/main/java/org/apache/iotdb/db/metadata/logfile/MLogWriter.java index ce48e5e0eb4f..8a24be878516 100644 --- a/server/src/main/java/org/apache/iotdb/db/metadata/logfile/MLogWriter.java +++ b/server/src/main/java/org/apache/iotdb/db/metadata/logfile/MLogWriter.java @@ -51,6 +51,7 @@ import org.apache.iotdb.tsfile.file.metadata.enums.TSEncoding; import org.apache.iotdb.tsfile.write.schema.MeasurementSchema; +import org.apache.commons.io.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -99,6 +100,14 @@ public void close() throws IOException { logWriter.close(); } + public synchronized void copyTo(File targetFile) throws IOException { + // flush the mlogBuffer + sync(); + // flush the os buffer + force(); + FileUtils.copyFile(logFile, targetFile); + } + private void sync() { try { logWriter.write(mlogBuffer); diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/schemaregion/ISchemaRegion.java b/server/src/main/java/org/apache/iotdb/db/metadata/schemaregion/ISchemaRegion.java index fca406d11c77..4ec058a0c053 100644 --- a/server/src/main/java/org/apache/iotdb/db/metadata/schemaregion/ISchemaRegion.java +++ b/server/src/main/java/org/apache/iotdb/db/metadata/schemaregion/ISchemaRegion.java @@ -26,7 +26,6 @@ import org.apache.iotdb.db.metadata.LocalSchemaProcessor; import org.apache.iotdb.db.metadata.mnode.IMNode; import org.apache.iotdb.db.metadata.mnode.IMeasurementMNode; -import org.apache.iotdb.db.metadata.mnode.IStorageGroupMNode; import org.apache.iotdb.db.metadata.path.MeasurementPath; import org.apache.iotdb.db.metadata.template.Template; import org.apache.iotdb.db.qp.physical.crud.InsertPlan; @@ -43,6 +42,7 @@ import org.apache.iotdb.db.query.dataset.ShowTimeSeriesResult; import org.apache.iotdb.tsfile.utils.Pair; +import java.io.File; import java.io.IOException; import java.util.List; import java.util.Map; @@ -76,7 +76,7 @@ public interface ISchemaRegion { // region Interfaces for initialization、recover and clear - void init(IStorageGroupMNode storageGroupMNode) throws MetadataException; + void init() throws MetadataException; /** clear all metadata components of this schemaRegion */ void clear(); @@ -91,6 +91,10 @@ public interface ISchemaRegion { // delete this schemaRegion and clear all resources void deleteSchemaRegion() throws MetadataException; + + boolean createSnapshot(File snapshotDir); + + void loadSnapshot(File latestSnapshotRootDir); // endregion // region Interfaces for Timeseries operation diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/schemaregion/SchemaRegionMemoryImpl.java b/server/src/main/java/org/apache/iotdb/db/metadata/schemaregion/SchemaRegionMemoryImpl.java index a7bc5e15f512..84815a938316 100644 --- a/server/src/main/java/org/apache/iotdb/db/metadata/schemaregion/SchemaRegionMemoryImpl.java +++ b/server/src/main/java/org/apache/iotdb/db/metadata/schemaregion/SchemaRegionMemoryImpl.java @@ -84,6 +84,7 @@ import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.LoadingCache; +import org.apache.commons.io.FileUtils; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.Logger; @@ -159,6 +160,8 @@ public class SchemaRegionMemoryImpl implements ISchemaRegion { private TimeseriesStatistics timeseriesStatistics = TimeseriesStatistics.getInstance(); private MemoryStatistics memoryStatistics = MemoryStatistics.getInstance(); + + private final IStorageGroupMNode storageGroupMNode; private MTreeBelowSGMemoryImpl mtree; // device -> DeviceMNode private LoadingCache mNodeCache; @@ -186,11 +189,12 @@ public SchemaRegionMemoryImpl( } }); - init(storageGroupMNode); + this.storageGroupMNode = storageGroupMNode; + init(); } @SuppressWarnings("squid:S2093") - public synchronized void init(IStorageGroupMNode storageGroupMNode) throws MetadataException { + public synchronized void init() throws MetadataException { if (initialized) { return; } @@ -409,6 +413,79 @@ public synchronized void deleteSchemaRegion() throws MetadataException { SchemaRegionUtils.deleteSchemaRegionFolder(schemaRegionDirPath, logger); } + @Override + public synchronized boolean createSnapshot(File snapshotDir) { + File mLogSnapshotTmp = + SystemFileFactory.INSTANCE.getFile(snapshotDir, MetadataConstant.METADATA_LOG_SNAPSHOT_TMP); + File mLogSnapshot = + SystemFileFactory.INSTANCE.getFile(snapshotDir, MetadataConstant.METADATA_LOG_SNAPSHOT); + + try { + logWriter.copyTo(mLogSnapshotTmp); + if (mLogSnapshot.exists() && !mLogSnapshot.delete()) { + logger.error( + "Failed to delete old snapshot file {} while creating snapshot for schemaRegion {}.", + mLogSnapshot.getName(), + schemaRegionId); + return false; + } + + if (!mLogSnapshotTmp.renameTo(mLogSnapshot)) { + logger.error( + "Failed to rename {} to {} while creating snapshot for schemaRegion {}.", + mLogSnapshotTmp.getName(), + mLogSnapshot.getName(), + schemaRegionId); + mLogSnapshot.delete(); + return false; + } + + return tagManager.createSnapshot(snapshotDir); + } catch (IOException e) { + logger.error( + "Failed to create snapshot for schemaRegion {} due to {}", + schemaRegionId, + e.getMessage(), + e); + mLogSnapshot.delete(); + return false; + } finally { + mLogSnapshotTmp.delete(); + } + } + + @Override + public void loadSnapshot(File latestSnapshotRootDir) { + File mLogSnapshot = + SystemFileFactory.INSTANCE.getFile( + latestSnapshotRootDir, MetadataConstant.METADATA_LOG_SNAPSHOT); + File tagSnapshot = + SystemFileFactory.INSTANCE.getFile( + latestSnapshotRootDir, MetadataConstant.TAG_LOG_SNAPSHOT); + + clear(); + + File mLog = + SystemFileFactory.INSTANCE.getFile(schemaRegionDirPath, MetadataConstant.METADATA_LOG); + File tagFile = + SystemFileFactory.INSTANCE.getFile(schemaRegionDirPath, MetadataConstant.TAG_LOG); + mLog.delete(); + tagFile.delete(); + + try { + FileUtils.copyFile(mLogSnapshot, mLog); + FileUtils.copyFile(tagSnapshot, tagFile); + + init(); + } catch (IOException | MetadataException e) { + logger.error( + "Failed to load snapshot for schemaRegion {} ue to {}", + schemaRegionId, + e.getMessage(), + e); + } + } + // endregion // region Interfaces and Implementation for Timeseries operation diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/schemaregion/SchemaRegionSchemaFileImpl.java b/server/src/main/java/org/apache/iotdb/db/metadata/schemaregion/SchemaRegionSchemaFileImpl.java index dbf30c00f0d7..86ec1229f0ea 100644 --- a/server/src/main/java/org/apache/iotdb/db/metadata/schemaregion/SchemaRegionSchemaFileImpl.java +++ b/server/src/main/java/org/apache/iotdb/db/metadata/schemaregion/SchemaRegionSchemaFileImpl.java @@ -161,6 +161,8 @@ public class SchemaRegionSchemaFileImpl implements ISchemaRegion { private TimeseriesStatistics timeseriesStatistics = TimeseriesStatistics.getInstance(); private MemoryStatistics memoryStatistics = MemoryStatistics.getInstance(); + + private final IStorageGroupMNode storageGroupMNode; private MTreeBelowSGCachedImpl mtree; // device -> DeviceMNode private LoadingCache mNodeCache; @@ -194,12 +196,12 @@ public SchemaRegionSchemaFileImpl( return mtree.getNodeByPath(partialPath); } }); - - init(storageGroupMNode); + this.storageGroupMNode = storageGroupMNode; + init(); } @SuppressWarnings("squid:S2093") - public synchronized void init(IStorageGroupMNode storageGroupMNode) throws MetadataException { + public synchronized void init() throws MetadataException { if (initialized) { return; } @@ -420,6 +422,20 @@ public synchronized void deleteSchemaRegion() throws MetadataException { SchemaRegionUtils.deleteSchemaRegionFolder(schemaRegionDirPath, logger); } + @Override + public boolean createSnapshot(File snapshotDir) { + // todo implement this + throw new UnsupportedOperationException( + "Schema_File mode currently doesn't support snapshot feature."); + } + + @Override + public void loadSnapshot(File latestSnapshotRootDir) { + // todo implement this + throw new UnsupportedOperationException( + "Schema_File mode currently doesn't support snapshot feature."); + } + // endregion // region Interfaces and Implementation for Timeseries operation diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/tag/TagLogFile.java b/server/src/main/java/org/apache/iotdb/db/metadata/tag/TagLogFile.java index 2fa968ddf780..a0861a245437 100644 --- a/server/src/main/java/org/apache/iotdb/db/metadata/tag/TagLogFile.java +++ b/server/src/main/java/org/apache/iotdb/db/metadata/tag/TagLogFile.java @@ -24,6 +24,7 @@ import org.apache.iotdb.tsfile.utils.Pair; import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils; +import org.apache.commons.io.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,6 +41,7 @@ public class TagLogFile implements AutoCloseable { private static final Logger logger = LoggerFactory.getLogger(TagLogFile.class); + private File tagFile; private FileChannel fileChannel; private static final String LENGTH_EXCEED_MSG = "Tag/Attribute exceeds the max length limit. " @@ -63,11 +65,11 @@ public TagLogFile(String schemaDir, String logFileName) throws IOException { } } - File logFile = SystemFileFactory.INSTANCE.getFile(schemaDir + File.separator + logFileName); + tagFile = SystemFileFactory.INSTANCE.getFile(schemaDir + File.separator + logFileName); this.fileChannel = FileChannel.open( - logFile.toPath(), + tagFile.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE); @@ -79,6 +81,12 @@ public TagLogFile(String schemaDir, String logFileName) throws IOException { } } + public synchronized void copyTo(File targetFile) throws IOException { + // flush os buffer + fileChannel.force(true); + FileUtils.copyFile(tagFile, targetFile); + } + /** @return tags map, attributes map */ public Pair, Map> read(int size, long position) throws IOException { diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/tag/TagManager.java b/server/src/main/java/org/apache/iotdb/db/metadata/tag/TagManager.java index c368b3a0d983..4f081c77de54 100644 --- a/server/src/main/java/org/apache/iotdb/db/metadata/tag/TagManager.java +++ b/server/src/main/java/org/apache/iotdb/db/metadata/tag/TagManager.java @@ -19,6 +19,7 @@ package org.apache.iotdb.db.metadata.tag; import org.apache.iotdb.commons.exception.MetadataException; +import org.apache.iotdb.commons.file.SystemFileFactory; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.conf.IoTDBConfig; import org.apache.iotdb.db.conf.IoTDBDescriptor; @@ -38,6 +39,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; @@ -73,6 +75,38 @@ public TagManager(String sgSchemaDirPath) throws IOException { tagLogFile = new TagLogFile(sgSchemaDirPath, MetadataConstant.TAG_LOG); } + public synchronized boolean createSnapshot(File targetDir) { + File tagLogSnapshot = + SystemFileFactory.INSTANCE.getFile(targetDir, MetadataConstant.TAG_LOG_SNAPSHOT); + File tagLogSnapshotTmp = + SystemFileFactory.INSTANCE.getFile(targetDir, MetadataConstant.TAG_LOG_SNAPSHOT_TMP); + try { + tagLogFile.copyTo(tagLogSnapshotTmp); + if (tagLogSnapshot.exists() && !tagLogSnapshot.delete()) { + logger.error( + "Failed to delete old snapshot {} while creating tagManager snapshot.", + tagLogSnapshot.getName()); + return false; + } + if (!tagLogSnapshotTmp.renameTo(tagLogSnapshot)) { + logger.error( + "Failed to rename {} to {} while creating tagManager snapshot.", + tagLogSnapshotTmp.getName(), + tagLogSnapshot.getName()); + tagLogSnapshot.delete(); + return false; + } + + return true; + } catch (IOException e) { + logger.error("Failed to create tagManager snapshot due to {}", e.getMessage(), e); + tagLogSnapshot.delete(); + return false; + } finally { + tagLogSnapshotTmp.delete(); + } + } + public boolean recoverIndex(long offset, IMeasurementMNode measurementMNode) throws IOException { Map tags = tagLogFile.readTag(config.getTagAttributeTotalSize(), offset); if (tags == null || tags.isEmpty()) { diff --git a/server/src/test/java/org/apache/iotdb/db/metadata/schemaRegion/SchemaRegionTest.java b/server/src/test/java/org/apache/iotdb/db/metadata/schemaRegion/SchemaRegionTest.java new file mode 100644 index 000000000000..a8c8498c8bc8 --- /dev/null +++ b/server/src/test/java/org/apache/iotdb/db/metadata/schemaRegion/SchemaRegionTest.java @@ -0,0 +1,100 @@ +/* + * 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.iotdb.db.metadata.schemaRegion; + +import org.apache.iotdb.commons.consensus.SchemaRegionId; +import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.db.conf.IoTDBConfig; +import org.apache.iotdb.db.conf.IoTDBDescriptor; +import org.apache.iotdb.db.metadata.schemaregion.ISchemaRegion; +import org.apache.iotdb.db.metadata.schemaregion.SchemaEngine; +import org.apache.iotdb.db.qp.physical.sys.CreateTimeSeriesPlan; +import org.apache.iotdb.db.qp.physical.sys.ShowTimeSeriesPlan; +import org.apache.iotdb.db.query.dataset.ShowTimeSeriesResult; +import org.apache.iotdb.db.utils.EnvironmentUtils; +import org.apache.iotdb.tsfile.file.metadata.enums.CompressionType; +import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; +import org.apache.iotdb.tsfile.file.metadata.enums.TSEncoding; +import org.apache.iotdb.tsfile.utils.Pair; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class SchemaRegionTest { + + SchemaEngine schemaEngine = SchemaEngine.getInstance(); + IoTDBConfig config = IoTDBDescriptor.getInstance().getConfig(); + + @Before + public void setUp() { + EnvironmentUtils.envSetUp(); + } + + @After + public void tearDown() throws Exception { + EnvironmentUtils.cleanEnv(); + } + + @Test + public void testSnapshot() throws Exception { + PartialPath storageGroup = new PartialPath("root.sg"); + SchemaRegionId schemaRegionId = new SchemaRegionId(0); + schemaEngine.createSchemaRegion(storageGroup, schemaRegionId); + ISchemaRegion schemaRegion = SchemaEngine.getInstance().getSchemaRegion(schemaRegionId); + + Map tags = new HashMap<>(); + tags.put("tag-key", "tag-value"); + schemaRegion.createTimeseries( + new CreateTimeSeriesPlan( + new PartialPath("root.sg.d1.s1"), + TSDataType.INT32, + TSEncoding.PLAIN, + CompressionType.UNCOMPRESSED, + null, + tags, + null, + null), + -1); + + File snapshotDir = new File(config.getSchemaDir() + File.separator + "snapshot"); + schemaRegion.createSnapshot(snapshotDir); + + schemaRegion.loadSnapshot(snapshotDir); + + Pair, Integer> result = + schemaRegion.showTimeseries( + new ShowTimeSeriesPlan( + new PartialPath("root.sg.**"), false, "tag-key", "tag-value", 0, 0, false), + null); + + ShowTimeSeriesResult seriesResult = result.left.get(0); + Assert.assertEquals(new PartialPath("root.sg.d1.s1").getFullPath(), seriesResult.getName()); + Map resultTagMap = seriesResult.getTag(); + Assert.assertEquals(1, resultTagMap.size()); + Assert.assertEquals("tag-value", resultTagMap.get("tag-key")); + } +} From f78e90fdbdaaaae1ee5262955ccc861b91f8a785 Mon Sep 17 00:00:00 2001 From: "Zhang.Jinrui" Date: Wed, 18 May 2022 17:49:49 +0800 Subject: [PATCH 042/436] Add distribution plan logic for AlignedSeriesScan (#5941) --- .../mpp/plan/planner/DistributionPlanner.java | 53 +++++++++++++++---- .../node/source/AlignedSeriesScanNode.java | 12 ++++- 2 files changed, 54 insertions(+), 11 deletions(-) diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/DistributionPlanner.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/DistributionPlanner.java index f0eb1fb38736..ef0a8b96cd1e 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/DistributionPlanner.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/DistributionPlanner.java @@ -43,8 +43,10 @@ import org.apache.iotdb.db.mpp.plan.planner.plan.node.process.ExchangeNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.process.TimeJoinNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.sink.FragmentSinkNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.source.AlignedSeriesScanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.source.SeriesAggregationScanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.source.SeriesScanNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.source.SourceNode; import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.AggregationDescriptor; import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.AggregationStep; import org.apache.iotdb.db.mpp.plan.statement.crud.QueryStatement; @@ -234,16 +236,10 @@ public PlanNode visitSeriesScan(SeriesScanNode node, DistributionPlanContext con return timeJoinNode; } - @Override public PlanNode visitSeriesAggregationScan( SeriesAggregationScanNode node, DistributionPlanContext context) { List dataDistribution = analysis.getPartitionInfo(node.getSeriesPath(), node.getTimeFilter()); - if (dataDistribution.size() == 1) { - node.setRegionReplicaSet(dataDistribution.get(0)); - return node; - } - List leafAggDescriptorList = new ArrayList<>(); node.getAggregationDescriptorList() .forEach( @@ -279,6 +275,26 @@ public PlanNode visitSeriesAggregationScan( return aggregationNode; } + @Override + public PlanNode visitAlignedSeriesScan( + AlignedSeriesScanNode node, DistributionPlanContext context) { + List dataDistribution = + analysis.getPartitionInfo(node.getAlignedPath(), node.getTimeFilter()); + if (dataDistribution.size() == 1) { + node.setRegionReplicaSet(dataDistribution.get(0)); + return node; + } + TimeJoinNode timeJoinNode = + new TimeJoinNode(context.queryContext.getQueryId().genPlanNodeId(), node.getScanOrder()); + for (TRegionReplicaSet dataRegion : dataDistribution) { + AlignedSeriesScanNode split = (AlignedSeriesScanNode) node.clone(); + split.setPlanNodeId(context.queryContext.getQueryId().genPlanNodeId()); + split.setRegionReplicaSet(dataRegion); + timeJoinNode.addChild(split); + } + return timeJoinNode; + } + @Override public PlanNode visitSchemaFetchMerge( SchemaFetchMergeNode node, DistributionPlanContext context) { @@ -314,7 +330,7 @@ public PlanNode visitTimeJoin(TimeJoinNode node, DistributionPlanContext context // Step 1: Get all source nodes. For the node which is not source, add it as the child of // current TimeJoinNode - List sources = new ArrayList<>(); + List sources = new ArrayList<>(); for (PlanNode child : node.getChildren()) { if (child instanceof SeriesScanNode) { // If the child is SeriesScanNode, we need to check whether this node should be seperated @@ -330,6 +346,18 @@ public PlanNode visitTimeJoin(TimeJoinNode node, DistributionPlanContext context split.setRegionReplicaSet(dataRegion); sources.add(split); } + } else if (child instanceof AlignedSeriesScanNode) { + AlignedSeriesScanNode handle = (AlignedSeriesScanNode) child; + List dataDistribution = + analysis.getPartitionInfo(handle.getAlignedPath(), handle.getTimeFilter()); + // If the size of dataDistribution is m, this SeriesScanNode should be seperated into m + // SeriesScanNode. + for (TRegionReplicaSet dataRegion : dataDistribution) { + AlignedSeriesScanNode split = (AlignedSeriesScanNode) handle.clone(); + split.setPlanNodeId(context.queryContext.getQueryId().genPlanNodeId()); + split.setRegionReplicaSet(dataRegion); + sources.add(split); + } } else if (child instanceof SeriesAggregationScanNode) { // TODO: (xingtanzjr) We should do the same thing for SeriesAggregateScanNode. Consider to // make SeriesAggregateScanNode @@ -344,8 +372,8 @@ public PlanNode visitTimeJoin(TimeJoinNode node, DistributionPlanContext context } // Step 2: For the source nodes, group them by the DataRegion. - Map> sourceGroup = - sources.stream().collect(Collectors.groupingBy(SeriesScanNode::getRegionReplicaSet)); + Map> sourceGroup = + sources.stream().collect(Collectors.groupingBy(SourceNode::getRegionReplicaSet)); // Step 3: For the source nodes which belong to same data region, add a TimeJoinNode for them // and make the // new TimeJoinNode as the child of current TimeJoinNode @@ -481,6 +509,13 @@ public PlanNode visitSeriesScan(SeriesScanNode node, NodeGroupContext context) { } @Override + public PlanNode visitAlignedSeriesScan(AlignedSeriesScanNode node, NodeGroupContext context) { + context.putNodeDistribution( + node.getPlanNodeId(), + new NodeDistribution(NodeDistributionType.NO_CHILD, node.getRegionReplicaSet())); + return node.clone(); + } + public PlanNode visitSeriesAggregationScan( SeriesAggregationScanNode node, NodeGroupContext context) { context.putNodeDistribution( diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/source/AlignedSeriesScanNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/source/AlignedSeriesScanNode.java index 749c653e0b25..72c2a2424483 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/source/AlignedSeriesScanNode.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/source/AlignedSeriesScanNode.java @@ -129,11 +129,13 @@ public void open() throws Exception {} @Override public TRegionReplicaSet getRegionReplicaSet() { - return null; + return regionReplicaSet; } @Override - public void setRegionReplicaSet(TRegionReplicaSet regionReplicaSet) {} + public void setRegionReplicaSet(TRegionReplicaSet regionReplicaSet) { + this.regionReplicaSet = regionReplicaSet; + } @Override public void close() throws Exception {} @@ -265,4 +267,10 @@ public int hashCode() { offset, regionReplicaSet); } + + public String toString() { + return String.format( + "AlignedSeriesScanNode-%s:[SeriesPath: %s, DataRegion: %s]", + this.getPlanNodeId(), this.getAlignedPath(), this.getRegionReplicaSet()); + } } From 3e63945619e29bf19e45ec35752a13f6ad6ab4a2 Mon Sep 17 00:00:00 2001 From: Alan Choo <43991780+HeimingZ@users.noreply.github.com> Date: Wed, 18 May 2022 20:32:15 +0800 Subject: [PATCH 043/436] [IOTDB-3196] Add search index in InsertNode (#5945) * add search index in insert node * add index * fix ci --- .../node/write/InsertMultiTabletsNode.java | 10 +++++++ .../planner/plan/node/write/InsertNode.java | 30 +++++++++++++++++++ .../plan/node/write/InsertRowNode.java | 4 ++- .../plan/node/write/InsertRowsNode.java | 10 +++++++ .../node/write/InsertRowsOfOneDeviceNode.java | 10 +++++++ .../plan/node/write/InsertTabletNode.java | 3 ++ .../node/write/InsertTabletNodeSerdeTest.java | 2 +- 7 files changed, 67 insertions(+), 2 deletions(-) diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertMultiTabletsNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertMultiTabletsNode.java index 13cf2319232e..8f3f0200c4a4 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertMultiTabletsNode.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertMultiTabletsNode.java @@ -116,6 +116,16 @@ public void addInsertTabletNode(InsertTabletNode node, Integer parentIndex) { parentInsertTabletNodeIndexList.add(parentIndex); } + @Override + public void setSearchIndex(long index) { + insertTabletNodeList.forEach(plan -> plan.setSearchIndex(index)); + } + + @Override + public void setSafelyDeletedSearchIndex(long index) { + insertTabletNodeList.forEach(plan -> plan.setSafelyDeletedSearchIndex(index)); + } + @Override public boolean validateAndSetSchema(SchemaTree schemaTree) { for (InsertTabletNode insertTabletNode : insertTabletNodeList) { diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertNode.java index 2b79cb36a39c..1fa2195dde6f 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertNode.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertNode.java @@ -44,6 +44,10 @@ import java.util.stream.Collectors; public abstract class InsertNode extends WritePlanNode implements IConsensusRequest { + /** this insert node doesn't need to participate in multi-leader consensus */ + public static final long NO_CONSENSUS_INDEX = -1; + /** no multi-leader consensus, all insert nodes can be safely deleted */ + public static final long DEFAULT_SAFELY_DELETED_SEARCH_INDEX = Long.MAX_VALUE; /** * if use id table, this filed is id form of device path
@@ -67,6 +71,14 @@ public abstract class InsertNode extends WritePlanNode implements IConsensusRequ */ protected IDeviceID deviceID; + /** this index is used by wal search, its order should be protected by the upper layer */ + protected long searchIndex = NO_CONSENSUS_INDEX; + /** + * this index pass info to wal, indicating that insert nodes whose search index are before this + * value can be deleted safely + */ + protected long safelyDeletedSearchIndex = DEFAULT_SAFELY_DELETED_SEARCH_INDEX; + /** Physical address of data region after splitting */ TRegionReplicaSet dataRegionReplicaSet; @@ -139,6 +151,22 @@ public void setDeviceID(IDeviceID deviceID) { this.deviceID = deviceID; } + public long getSearchIndex() { + return searchIndex; + } + + public void setSearchIndex(long searchIndex) { + this.searchIndex = searchIndex; + } + + public long getSafelyDeletedSearchIndex() { + return safelyDeletedSearchIndex; + } + + public void setSafelyDeletedSearchIndex(long safelyDeletedSearchIndex) { + this.safelyDeletedSearchIndex = safelyDeletedSearchIndex; + } + /** * Deserialize via {@link * org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeType#deserialize(ByteBuffer)} @@ -153,6 +181,7 @@ protected void serializeAttributes(ByteBuffer byteBuffer) { throw new NotImplementedException("serializeAttributes of InsertNode is not implemented"); } + // region Serialization methods for WAL /** Serialized size of measurement schemas, ignoring failed time series */ protected int serializeMeasurementSchemasSize() { int byteLen = 0; @@ -187,6 +216,7 @@ protected void deserializeMeasurementSchemas(DataInputStream stream) throws IOEx measurements[i] = measurementSchemas[i].getMeasurementId(); } } + // endregion public TRegionReplicaSet getRegionReplicaSet() { return dataRegionReplicaSet; diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertRowNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertRowNode.java index 61a808190972..0ab68df815ad 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertRowNode.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertRowNode.java @@ -422,7 +422,7 @@ public int serializedSize() { private int subSerializeSize() { int size = 0; - size += Long.BYTES; + size += Long.BYTES * 2; size += ReadWriteIOUtils.sizeToWrite(devicePath.getFullPath()); return size + serializeMeasurementsAndValuesSize(); } @@ -478,6 +478,7 @@ public void serializeToWAL(IWALByteBufferView buffer) { } private void subSerialize(IWALByteBufferView buffer) { + buffer.putLong(searchIndex); buffer.putLong(time); WALWriteUtils.write(devicePath.getFullPath(), buffer); serializeMeasurementsAndValues(buffer); @@ -534,6 +535,7 @@ public static InsertRowNode deserialize(DataInputStream stream) throws IOException, IllegalPathException { // we do not store plan node id in wal entry InsertRowNode insertNode = new InsertRowNode(new PlanNodeId("")); + insertNode.setSearchIndex(stream.readLong()); insertNode.setTime(stream.readLong()); insertNode.setDevicePath(new PartialPath(ReadWriteIOUtils.readString(stream))); insertNode.deserializeMeasurementsAndValues(stream); diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertRowsNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertRowsNode.java index 5ca6141a8de1..e3c82a5e3ad5 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertRowsNode.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertRowsNode.java @@ -86,6 +86,16 @@ public void addOneInsertRowNode(InsertRowNode node, int index) { insertRowNodeIndexList.add(index); } + @Override + public void setSearchIndex(long index) { + insertRowNodeList.forEach(plan -> plan.setSearchIndex(index)); + } + + @Override + public void setSafelyDeletedSearchIndex(long index) { + insertRowNodeList.forEach(plan -> plan.setSafelyDeletedSearchIndex(index)); + } + public Map getResults() { return results; } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertRowsOfOneDeviceNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertRowsOfOneDeviceNode.java index eaa8d2f2a8d2..2fd932c4bd1d 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertRowsOfOneDeviceNode.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertRowsOfOneDeviceNode.java @@ -75,6 +75,16 @@ public Map getResults() { return results; } + @Override + public void setSearchIndex(long index) { + insertRowNodeList.forEach(plan -> plan.setSearchIndex(index)); + } + + @Override + public void setSafelyDeletedSearchIndex(long index) { + insertRowNodeList.forEach(plan -> plan.setSafelyDeletedSearchIndex(index)); + } + public TSStatus[] getFailingStatus() { return StatusUtils.getFailingStatus(results, insertRowNodeList.size()); } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertTabletNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertTabletNode.java index 24b560cc9a32..160b1a94e8d6 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertTabletNode.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertTabletNode.java @@ -537,6 +537,7 @@ public int serializedSize(int start, int end) { int subSerializeSize(int start, int end) { int size = 0; + size += Long.BYTES; size += ReadWriteIOUtils.sizeToWrite(devicePath.getFullPath()); // measurements size size += Integer.BYTES; @@ -612,6 +613,7 @@ public void serializeToWAL(IWALByteBufferView buffer, int start, int end) { } void subSerialize(IWALByteBufferView buffer, int start, int end) { + buffer.putLong(searchIndex); WALWriteUtils.write(devicePath.getFullPath(), buffer); // data types are serialized in measurement schemas writeMeasurementSchemas(buffer); @@ -723,6 +725,7 @@ public static InsertTabletNode deserialize(DataInputStream stream) } private void subDeserialize(DataInputStream stream) throws IllegalPathException, IOException { + searchIndex = stream.readLong(); devicePath = new PartialPath(ReadWriteIOUtils.readString(stream)); int measurementSize = stream.readInt(); diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/write/InsertTabletNodeSerdeTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/write/InsertTabletNodeSerdeTest.java index b1c73bc080c3..b6604aedb321 100644 --- a/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/write/InsertTabletNodeSerdeTest.java +++ b/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/write/InsertTabletNodeSerdeTest.java @@ -61,7 +61,7 @@ public void testSerializeAndDeserialize() throws IllegalPathException { } @Test - public void TestSerializeAndDeserializeForWAL() throws IllegalPathException, IOException { + public void testSerializeAndDeserializeForWAL() throws IllegalPathException, IOException { InsertTabletNode insertTabletNode = getInsertTabletNodeWithSchema(); int serializedSize = insertTabletNode.serializedSize(); From 406a79f08ceaa9b97d270d28d4ab210303fa4715 Mon Sep 17 00:00:00 2001 From: Liao Lanyu <48237151+Plutooooooo@users.noreply.github.com> Date: Wed, 18 May 2022 23:56:11 +0800 Subject: [PATCH 044/436] [IOTDB-3101] definition of attributekey and attributevalue (#5888) --- .../antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 | 2 +- .../org/apache/iotdb/db/integration/IoTDBTagAlterIT.java | 4 ++-- .../org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java | 8 +++----- .../java/org/apache/iotdb/db/qp/sql/IoTDBSqlVisitor.java | 8 +++----- 4 files changed, 9 insertions(+), 13 deletions(-) diff --git a/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 b/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 index d1b1565ceca8..f513a4da7a7f 100644 --- a/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 +++ b/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 @@ -161,7 +161,7 @@ alterTimeseries alterClause : RENAME beforeName=attributeKey TO currentName=attributeKey | SET attributePair (COMMA attributePair)* - | DROP STRING_LITERAL (COMMA STRING_LITERAL)* + | DROP attributeKey (COMMA attributeKey)* | ADD TAGS attributePair (COMMA attributePair)* | ADD ATTRIBUTES attributePair (COMMA attributePair)* | UPSERT aliasClause? tagClause? attributeClause? diff --git a/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBTagAlterIT.java b/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBTagAlterIT.java index 0132ea28aba3..4bee6635c829 100644 --- a/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBTagAlterIT.java +++ b/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBTagAlterIT.java @@ -98,7 +98,7 @@ public void renameTest() { assertEquals(ret1.length, count); try { - statement.execute("ALTER timeseries root.turbine.d1.s1 RENAME 'tag3' TO 'tagNew3'"); + statement.execute("ALTER timeseries root.turbine.d1.s1 RENAME tag3 TO 'tagNew3'"); fail(); } catch (Exception e) { assertTrue( @@ -290,7 +290,7 @@ public void dropTest() { } assertEquals(ret.length, count); - statement.execute("ALTER timeseries root.turbine.d1.s1 DROP 'attr1','tag1'"); + statement.execute("ALTER timeseries root.turbine.d1.s1 DROP attr1,'tag1'"); hasResult = statement.execute("show timeseries"); assertTrue(hasResult); resultSet = statement.getResultSet(); diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java index 8f9db1267fe5..8d826f98f2d3 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java @@ -309,9 +309,7 @@ private void parseAlterClause( // rename if (ctx.RENAME() != null) { alterTimeSeriesStatement.setAlterType(AlterTimeSeriesStatement.AlterType.RENAME); - alterMap.put( - parseStringLiteral(ctx.beforeName.getText()), - parseStringLiteral(ctx.currentName.getText())); + alterMap.put(parseAttributeKey(ctx.beforeName), parseAttributeKey(ctx.currentName)); } else if (ctx.SET() != null) { // set alterTimeSeriesStatement.setAlterType(AlterTimeSeriesStatement.AlterType.SET); @@ -319,8 +317,8 @@ private void parseAlterClause( } else if (ctx.DROP() != null) { // drop alterTimeSeriesStatement.setAlterType(AlterTimeSeriesStatement.AlterType.DROP); - for (int i = 0; i < ctx.STRING_LITERAL().size(); i++) { - alterMap.put(parseStringLiteral(ctx.STRING_LITERAL(i).getText()), null); + for (int i = 0; i < ctx.attributeKey().size(); i++) { + alterMap.put(parseAttributeKey(ctx.attributeKey().get(i)), null); } } else if (ctx.TAGS() != null) { // add tag diff --git a/server/src/main/java/org/apache/iotdb/db/qp/sql/IoTDBSqlVisitor.java b/server/src/main/java/org/apache/iotdb/db/qp/sql/IoTDBSqlVisitor.java index da67a66f7ed4..a81179f435aa 100644 --- a/server/src/main/java/org/apache/iotdb/db/qp/sql/IoTDBSqlVisitor.java +++ b/server/src/main/java/org/apache/iotdb/db/qp/sql/IoTDBSqlVisitor.java @@ -655,9 +655,7 @@ private void parseAlterClause( // rename if (ctx.RENAME() != null) { alterTimeSeriesOperator.setAlterType(AlterType.RENAME); - alterMap.put( - parseStringLiteral(ctx.beforeName.getText()), - parseStringLiteral(ctx.currentName.getText())); + alterMap.put(parseAttributeKey(ctx.beforeName), parseAttributeKey(ctx.currentName)); } else if (ctx.SET() != null) { // set alterTimeSeriesOperator.setAlterType(AlterType.SET); @@ -665,8 +663,8 @@ private void parseAlterClause( } else if (ctx.DROP() != null) { // drop alterTimeSeriesOperator.setAlterType(AlterType.DROP); - for (int i = 0; i < ctx.STRING_LITERAL().size(); i++) { - alterMap.put(parseStringLiteral(ctx.STRING_LITERAL(i).getText()), null); + for (int i = 0; i < ctx.attributeKey().size(); i++) { + alterMap.put(parseAttributeKey(ctx.attributeKey().get(i)), null); } } else if (ctx.TAGS() != null) { // add tag From def4fb0fae79b1e29f9f4220f674b7bbd10f2970 Mon Sep 17 00:00:00 2001 From: Marcos_Zyk <38524330+MarcosZyk@users.noreply.github.com> Date: Thu, 19 May 2022 09:10:06 +0800 Subject: [PATCH 045/436] Modify the serialize/deserialize of MTreeAboveSG with stream & Add storageGroupNode to the result of MNodeAboveSGCollector (#5947) --- .../persistence/ClusterSchemaInfo.java | 25 +++------ .../utils/ThriftConfigNodeSerDeUtils.java | 34 ++++++++++++ .../iotdb/db/metadata/mtree/MTreeAboveSG.java | 55 ++++++++++--------- .../collector/MNodeAboveSGCollector.java | 6 +- .../db/metadata/mtree/MTreeAboveSGTest.java | 18 ++++-- 5 files changed, 87 insertions(+), 51 deletions(-) diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/ClusterSchemaInfo.java b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/ClusterSchemaInfo.java index 7f5d4f0b8341..12e56b46ee13 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/ClusterSchemaInfo.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/ClusterSchemaInfo.java @@ -46,12 +46,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -72,9 +72,6 @@ public class ClusterSchemaInfo implements SnapshotProcessor { private MTreeAboveSG mTree; - // The size of the buffer used for snapshot(temporary value) - private final int bufferSize = 10 * 1024 * 1024; - private final String snapshotFileName = "cluster_schema.bin"; public ClusterSchemaInfo() { @@ -455,19 +452,16 @@ public boolean processTakeSnapshot(File snapshotDir) throws IOException { } File tmpFile = new File(snapshotFile.getAbsolutePath() + "-" + UUID.randomUUID()); - ByteBuffer buffer = ByteBuffer.allocate(bufferSize); storageGroupReadWriteLock.readLock().lock(); try { try (FileOutputStream fileOutputStream = new FileOutputStream(tmpFile); - FileChannel fileChannel = fileOutputStream.getChannel()) { - mTree.serialize(buffer); - buffer.flip(); - fileChannel.write(buffer); + BufferedOutputStream outputStream = new BufferedOutputStream(fileOutputStream)) { + mTree.serialize(outputStream); + outputStream.flush(); } return tmpFile.renameTo(snapshotFile); } finally { - buffer.clear(); for (int retry = 0; retry < 5; retry++) { if (!tmpFile.exists() || tmpFile.delete()) { break; @@ -491,16 +485,11 @@ public void processLoadSnapshot(File snapshotDir) throws IOException { return; } storageGroupReadWriteLock.writeLock().lock(); - ByteBuffer buffer = ByteBuffer.allocate(bufferSize); try (FileInputStream fileInputStream = new FileInputStream(snapshotFile); - FileChannel fileChannel = fileInputStream.getChannel()) { - // get buffer from fileChannel - fileChannel.read(buffer); + BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream)) { mTree.clear(); - buffer.flip(); - mTree.deserialize(buffer); + mTree.deserialize(bufferedInputStream); } finally { - buffer.clear(); storageGroupReadWriteLock.writeLock().unlock(); } } diff --git a/node-commons/src/main/java/org/apache/iotdb/commons/utils/ThriftConfigNodeSerDeUtils.java b/node-commons/src/main/java/org/apache/iotdb/commons/utils/ThriftConfigNodeSerDeUtils.java index 2b7bef298620..25063bb7ce00 100644 --- a/node-commons/src/main/java/org/apache/iotdb/commons/utils/ThriftConfigNodeSerDeUtils.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/utils/ThriftConfigNodeSerDeUtils.java @@ -25,9 +25,12 @@ import org.apache.thrift.TException; import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.thrift.transport.TByteBuffer; +import org.apache.thrift.transport.TIOStreamTransport; import org.apache.thrift.transport.TTransport; import org.apache.thrift.transport.TTransportException; +import java.io.InputStream; +import java.io.OutputStream; import java.nio.ByteBuffer; /** Utils for serialize and deserialize all the data struct defined by thrift-confignode */ @@ -49,6 +52,18 @@ private static TBinaryProtocol generateReadProtocol(ByteBuffer buffer) return new TBinaryProtocol(transport); } + private static TBinaryProtocol generateWriteProtocol(OutputStream outputStream) + throws TTransportException { + TIOStreamTransport tioStreamTransport = new TIOStreamTransport(outputStream); + return new TBinaryProtocol(tioStreamTransport); + } + + private static TBinaryProtocol generateReadProtocol(InputStream inputStream) + throws TTransportException { + TIOStreamTransport tioStreamTransport = new TIOStreamTransport(inputStream); + return new TBinaryProtocol(tioStreamTransport); + } + public static void serializeTStorageGroupSchema( TStorageGroupSchema storageGroupSchema, ByteBuffer buffer) { try { @@ -68,6 +83,25 @@ public static TStorageGroupSchema deserializeTStorageGroupSchema(ByteBuffer buff return storageGroupSchema; } + public static void serializeTStorageGroupSchema( + TStorageGroupSchema storageGroupSchema, OutputStream outputStream) { + try { + storageGroupSchema.write(generateWriteProtocol(outputStream)); + } catch (TException e) { + throw new ThriftSerDeException("Write TStorageGroupSchema failed: ", e); + } + } + + public static TStorageGroupSchema deserializeTStorageGroupSchema(InputStream inputStream) { + TStorageGroupSchema storageGroupSchema = new TStorageGroupSchema(); + try { + storageGroupSchema.read(generateReadProtocol(inputStream)); + } catch (TException e) { + throw new ThriftSerDeException("Read TStorageGroupSchema failed: ", e); + } + return storageGroupSchema; + } + public static void serializeTConfigNodeLocation( TConfigNodeLocation configNodeLocation, ByteBuffer buffer) { try { diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/MTreeAboveSG.java b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/MTreeAboveSG.java index e63b30146fa9..2d04d6130cf6 100644 --- a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/MTreeAboveSG.java +++ b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/MTreeAboveSG.java @@ -47,7 +47,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.nio.ByteBuffer; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; @@ -529,39 +531,41 @@ protected void transferToResult(IMNode node) { } } - public void serialize(ByteBuffer buffer) { - serializeInternalNode((InternalMNode) this.root, buffer); + public void serialize(OutputStream outputStream) throws IOException { + serializeInternalNode((InternalMNode) this.root, outputStream); } - private void serializeInternalNode(InternalMNode node, ByteBuffer buffer) { + private void serializeInternalNode(InternalMNode node, OutputStream outputStream) + throws IOException { for (IMNode child : node.getChildren().values()) { if (child.isStorageGroup()) { - serializeStorageGroupNode((StorageGroupMNode) child, buffer); + serializeStorageGroupNode((StorageGroupMNode) child, outputStream); } else { - serializeInternalNode((InternalMNode) child, buffer); + serializeInternalNode((InternalMNode) child, outputStream); } } - buffer.put(INTERNAL_MNODE_TYPE); - ReadWriteIOUtils.write(node.getName(), buffer); - ReadWriteIOUtils.write(node.getChildren().size(), buffer); + ReadWriteIOUtils.write(INTERNAL_MNODE_TYPE, outputStream); + ReadWriteIOUtils.write(node.getName(), outputStream); + ReadWriteIOUtils.write(node.getChildren().size(), outputStream); } - private void serializeStorageGroupNode(StorageGroupMNode storageGroupNode, ByteBuffer buffer) { - buffer.put(STORAGE_GROUP_MNODE_TYPE); - ReadWriteIOUtils.write(storageGroupNode.getName(), buffer); + private void serializeStorageGroupNode( + StorageGroupMNode storageGroupNode, OutputStream outputStream) throws IOException { + ReadWriteIOUtils.write(STORAGE_GROUP_MNODE_TYPE, outputStream); + ReadWriteIOUtils.write(storageGroupNode.getName(), outputStream); ThriftConfigNodeSerDeUtils.serializeTStorageGroupSchema( - storageGroupNode.getStorageGroupSchema(), buffer); + storageGroupNode.getStorageGroupSchema(), outputStream); } - public void deserialize(ByteBuffer buffer) { - byte type = buffer.get(); + public void deserialize(InputStream inputStream) throws IOException { + byte type = ReadWriteIOUtils.readByte(inputStream); if (type != STORAGE_GROUP_MNODE_TYPE) { logger.error("Wrong node type. Cannot deserialize MTreeAboveSG from given buffer"); return; } - StorageGroupMNode storageGroupMNode = deserializeStorageGroupMNode(buffer); + StorageGroupMNode storageGroupMNode = deserializeStorageGroupMNode(inputStream); InternalMNode internalMNode; Stack stack = new Stack<>(); @@ -571,11 +575,11 @@ public void deserialize(ByteBuffer buffer) { int childNum = 0; while (!PATH_ROOT.equals(name)) { - type = buffer.get(); + type = ReadWriteIOUtils.readByte(inputStream); switch (type) { case INTERNAL_MNODE_TYPE: - internalMNode = deserializeInternalMNode(buffer); - childNum = ReadWriteIOUtils.readInt(buffer); + internalMNode = deserializeInternalMNode(inputStream); + childNum = ReadWriteIOUtils.readInt(inputStream); while (childNum > 0) { internalMNode.addChild(stack.pop()); childNum--; @@ -584,7 +588,7 @@ public void deserialize(ByteBuffer buffer) { name = internalMNode.getName(); break; case STORAGE_GROUP_MNODE_TYPE: - storageGroupMNode = deserializeStorageGroupMNode(buffer); + storageGroupMNode = deserializeStorageGroupMNode(inputStream); childNum = 0; stack.push(storageGroupMNode); name = storageGroupMNode.getName(); @@ -597,15 +601,16 @@ public void deserialize(ByteBuffer buffer) { this.root = stack.peek(); } - private InternalMNode deserializeInternalMNode(ByteBuffer buffer) { - return new InternalMNode(null, ReadWriteIOUtils.readString(buffer)); + private InternalMNode deserializeInternalMNode(InputStream inputStream) throws IOException { + return new InternalMNode(null, ReadWriteIOUtils.readString(inputStream)); } - private StorageGroupMNode deserializeStorageGroupMNode(ByteBuffer buffer) { + private StorageGroupMNode deserializeStorageGroupMNode(InputStream inputStream) + throws IOException { StorageGroupMNode storageGroupMNode = - new StorageGroupMNode(null, ReadWriteIOUtils.readString(buffer)); + new StorageGroupMNode(null, ReadWriteIOUtils.readString(inputStream)); storageGroupMNode.setStorageGroupSchema( - ThriftConfigNodeSerDeUtils.deserializeTStorageGroupSchema(buffer)); + ThriftConfigNodeSerDeUtils.deserializeTStorageGroupSchema(inputStream)); return storageGroupMNode; } } diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/collector/MNodeAboveSGCollector.java b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/collector/MNodeAboveSGCollector.java index 393cf44350ab..ec6ba13b9b13 100644 --- a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/collector/MNodeAboveSGCollector.java +++ b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/collector/MNodeAboveSGCollector.java @@ -37,20 +37,22 @@ public MNodeAboveSGCollector(IMNode startNode, PartialPath path, IMTreeStore sto @Override protected boolean processInternalMatchedMNode(IMNode node, int idx, int level) { + boolean shouldSkipSubtree = super.processInternalMatchedMNode(node, idx, level); if (node.isStorageGroup()) { involvedStorageGroupMNodes.add(node.getPartialPath()); return true; } - return super.processInternalMatchedMNode(node, idx, level); + return shouldSkipSubtree; } @Override protected boolean processFullMatchedMNode(IMNode node, int idx, int level) { + boolean shouldSkipSubtree = super.processFullMatchedMNode(node, idx, level); if (node.isStorageGroup()) { involvedStorageGroupMNodes.add(node.getPartialPath()); return true; } - return super.processFullMatchedMNode(node, idx, level); + return shouldSkipSubtree; } public Set getInvolvedStorageGroupMNodes() { diff --git a/server/src/test/java/org/apache/iotdb/db/metadata/mtree/MTreeAboveSGTest.java b/server/src/test/java/org/apache/iotdb/db/metadata/mtree/MTreeAboveSGTest.java index e357b9a0c05d..ce9467808a9f 100644 --- a/server/src/test/java/org/apache/iotdb/db/metadata/mtree/MTreeAboveSGTest.java +++ b/server/src/test/java/org/apache/iotdb/db/metadata/mtree/MTreeAboveSGTest.java @@ -32,8 +32,10 @@ import org.junit.Before; import org.junit.Test; -import java.nio.ByteBuffer; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -106,7 +108,7 @@ public void testGetAllChildNodeNamesByPath() { Set result2 = root.getChildNodeNameInNextLevel(new PartialPath("root.a")).left; Set result3 = root.getChildNodeNameInNextLevel(new PartialPath("root")).left; assertEquals(new HashSet<>(), result1); - assertEquals(new HashSet<>(Collections.emptyList()), result2); + assertEquals(new HashSet<>(Arrays.asList("d0", "d5")), result2); assertEquals(new HashSet<>(Collections.singletonList("a")), result3); // if child node is nll will return null HashSet @@ -275,6 +277,10 @@ public void testGetNodeListInLevel() throws MetadataException { Assert.assertEquals(0, result.left.size()); Assert.assertEquals(2, result.right.size()); + result = root.getNodesListInGivenLevel(new PartialPath("root.**"), 1, false, null); + Assert.assertEquals(2, result.left.size()); + Assert.assertEquals(2, result.right.size()); + result = root.getNodesListInGivenLevel(new PartialPath("root.*.*"), 2, false, null); Assert.assertEquals(0, result.left.size()); Assert.assertEquals(2, result.right.size()); @@ -311,12 +317,12 @@ public void testSerialization() throws Exception { storageGroupMNode.setTimePartitionInterval(i); } - ByteBuffer byteBuffer = ByteBuffer.allocate(1024 * 1024); - root.serialize(byteBuffer); - byteBuffer.flip(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + root.serialize(outputStream); MTreeAboveSG newTree = new MTreeAboveSG(); - newTree.deserialize(byteBuffer); + ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray()); + newTree.deserialize(inputStream); for (int i = 0; i < pathList.length; i++) { newTree.isStorageGroup(pathList[i]); From 1ebeac5b6a9a3bfcfee6825ff8bdf7d22ab04e6f Mon Sep 17 00:00:00 2001 From: Liu Xuxin <37140360+THUMarkLau@users.noreply.github.com> Date: Thu, 19 May 2022 09:24:14 +0800 Subject: [PATCH 046/436] [IOTDB-2977] Take and load snapshot for DataRegionStateMachine (#5923) --- .../iotdb/db/integration/IoTDBSnapshotIT.java | 259 ++++++++++++++++++ .../statemachine/DataRegionStateMachine.java | 38 ++- .../iotdb/db/engine/StorageEngineV2.java | 13 + .../db/engine/snapshot/SnapshotLoader.java | 196 +++++++++++++ .../db/engine/snapshot/SnapshotTaker.java | 166 +++++++++++ .../exception/DirectoryNotLegalException.java | 28 ++ .../db/engine/storagegroup/DataRegion.java | 4 + .../storagegroup/TsFileNameGenerator.java | 2 +- .../org/apache/iotdb/rpc/TSStatusCode.java | 1 + 9 files changed, 703 insertions(+), 4 deletions(-) create mode 100644 integration/src/test/java/org/apache/iotdb/db/integration/IoTDBSnapshotIT.java create mode 100644 server/src/main/java/org/apache/iotdb/db/engine/snapshot/SnapshotLoader.java create mode 100644 server/src/main/java/org/apache/iotdb/db/engine/snapshot/SnapshotTaker.java create mode 100644 server/src/main/java/org/apache/iotdb/db/engine/snapshot/exception/DirectoryNotLegalException.java diff --git a/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBSnapshotIT.java b/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBSnapshotIT.java new file mode 100644 index 000000000000..9b72f49c5f05 --- /dev/null +++ b/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBSnapshotIT.java @@ -0,0 +1,259 @@ +/* + * 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.iotdb.db.integration; + +import org.apache.iotdb.commons.consensus.DataRegionId; +import org.apache.iotdb.commons.exception.IllegalPathException; +import org.apache.iotdb.commons.exception.MetadataException; +import org.apache.iotdb.db.conf.IoTDBDescriptor; +import org.apache.iotdb.db.constant.TestConstant; +import org.apache.iotdb.db.engine.StorageEngineV2; +import org.apache.iotdb.db.engine.cache.ChunkCache; +import org.apache.iotdb.db.engine.cache.TimeSeriesMetadataCache; +import org.apache.iotdb.db.engine.snapshot.SnapshotLoader; +import org.apache.iotdb.db.engine.snapshot.SnapshotTaker; +import org.apache.iotdb.db.engine.snapshot.exception.DirectoryNotLegalException; +import org.apache.iotdb.db.engine.storagegroup.DataRegion; +import org.apache.iotdb.db.exception.StorageEngineException; +import org.apache.iotdb.integration.env.EnvFactory; + +import org.apache.commons.io.FileUtils; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.HashMap; +import java.util.Map; + +public class IoTDBSnapshotIT { + final String SG_NAME = "root.snapshotTest"; + + @Before + public void setUp() throws Exception { + EnvFactory.getEnv().initBeforeTest(); + IoTDBDescriptor.getInstance().getConfig().setEnableCrossSpaceCompaction(false); + } + + @After + public void tearDown() throws Exception { + EnvFactory.getEnv().cleanAfterTest(); + IoTDBDescriptor.getInstance().getConfig().setEnableCrossSpaceCompaction(true); + } + + @Test + public void testTakeSnapshot() + throws SQLException, IllegalPathException, StorageEngineException, IOException, + DirectoryNotLegalException { + try (Connection connection = EnvFactory.getEnv().getConnection(); + Statement statement = connection.createStatement()) { + statement.execute("set storage group to " + SG_NAME); + for (int i = 0; i < 10; ++i) { + for (int j = 0; j < 10; ++j) { + statement.execute( + String.format("insert into %s.d%d(time, s) values (%d, %d)", SG_NAME, i, j, j)); + } + statement.execute("flush"); + for (int j = 0; j < 10; ++j) { + statement.execute( + String.format("insert into %s.d%d(time, s) values (%d, %d)", SG_NAME, i, j, j + 1)); + } + statement.execute("flush"); + } + + DataRegion region = StorageEngineV2.getInstance().getDataRegion(new DataRegionId(0)); + File snapshotDir = new File(TestConstant.OUTPUT_DATA_DIR, "snapshot"); + if (snapshotDir.exists()) { + FileUtils.forceDelete(snapshotDir); + } + + new SnapshotTaker(region).takeFullSnapshot(snapshotDir.getAbsolutePath(), true); + + Assert.assertTrue(snapshotDir.exists()); + Assert.assertTrue(snapshotDir.isDirectory()); + File[] seqTsfiles = + snapshotDir.listFiles((dir, name) -> name.endsWith(".tsfile") && name.startsWith("seq")); + File[] unseqTsfiles = + snapshotDir.listFiles( + (dir, name) -> name.endsWith(".tsfile") && name.startsWith("unseq")); + File[] tsfileResources = + snapshotDir.listFiles((dir, name) -> name.endsWith(".tsfile.resource")); + Assert.assertNotNull(seqTsfiles); + Assert.assertNotNull(unseqTsfiles); + Assert.assertNotNull(tsfileResources); + Assert.assertEquals(10, seqTsfiles.length); + Assert.assertEquals(10, unseqTsfiles.length); + Assert.assertEquals(20, tsfileResources.length); + } + } + + @Test(expected = DirectoryNotLegalException.class) + public void testTakeSnapshotInNotEmptyDir() + throws SQLException, IOException, IllegalPathException, StorageEngineException, + DirectoryNotLegalException { + try (Connection connection = EnvFactory.getEnv().getConnection(); + Statement statement = connection.createStatement()) { + statement.execute("set storage group to " + SG_NAME); + for (int i = 0; i < 10; ++i) { + for (int j = 0; j < 10; ++j) { + statement.execute( + String.format("insert into %s.d%d(time, s) values (%d, %d)", SG_NAME, i, j, j)); + } + statement.execute("flush"); + for (int j = 0; j < 10; ++j) { + statement.execute( + String.format("insert into %s.d%d(time, s) values (%d, %d)", SG_NAME, i, j, j + 1)); + } + statement.execute("flush"); + } + + DataRegion region = StorageEngineV2.getInstance().getDataRegion(new DataRegionId(0)); + File snapshotDir = new File(TestConstant.OUTPUT_DATA_DIR, "snapshot"); + if (!snapshotDir.exists()) { + snapshotDir.mkdirs(); + } + + File tmpFile = new File(snapshotDir, "test"); + tmpFile.createNewFile(); + + new SnapshotTaker(region).takeFullSnapshot(snapshotDir.getAbsolutePath(), true); + } + } + + @Test + public void testLoadSnapshot() + throws SQLException, MetadataException, StorageEngineException, DirectoryNotLegalException, + IOException { + try (Connection connection = EnvFactory.getEnv().getConnection(); + Statement statement = connection.createStatement()) { + Map resultMap = new HashMap<>(); + statement.execute("set storage group to " + SG_NAME); + for (int i = 0; i < 10; ++i) { + for (int j = 0; j < 10; ++j) { + statement.execute( + String.format("insert into %s.d%d(time, s) values (%d, %d)", SG_NAME, i, j, j)); + } + statement.execute("flush"); + for (int j = 0; j < 10; ++j) { + statement.execute( + String.format("insert into %s.d%d(time, s) values (%d, %d)", SG_NAME, i, j, j + 1)); + } + statement.execute("flush"); + } + ResultSet resultSet = statement.executeQuery("select ** from root"); + while (resultSet.next()) { + long time = resultSet.getLong("Time"); + for (int i = 0; i < 10; ++i) { + String measurment = SG_NAME + ".d" + i + ".s"; + int res = resultSet.getInt(SG_NAME + ".d" + i + ".s"); + resultMap.put(time + measurment, res); + } + } + + DataRegion region = StorageEngineV2.getInstance().getDataRegion(new DataRegionId(0)); + File snapshotDir = new File(TestConstant.OUTPUT_DATA_DIR, "snapshot"); + if (!snapshotDir.exists()) { + snapshotDir.mkdirs(); + } + new SnapshotTaker(region).takeFullSnapshot(snapshotDir.getAbsolutePath(), true); + StorageEngineV2.getInstance() + .setDataRegion( + new DataRegionId(0), + new SnapshotLoader(snapshotDir.getAbsolutePath(), SG_NAME, "0") + .loadSnapshotForStateMachine()); + + ChunkCache.getInstance().clear(); + TimeSeriesMetadataCache.getInstance().clear(); + resultSet = statement.executeQuery("select ** from root"); + while (resultSet.next()) { + long time = resultSet.getLong("Time"); + for (int i = 0; i < 10; ++i) { + String measurment = SG_NAME + ".d" + i + ".s"; + int res = resultSet.getInt(SG_NAME + ".d" + i + ".s"); + Assert.assertEquals(resultMap.get(time + measurment).intValue(), res); + } + } + } + } + + @Test + public void testTakeAndLoadSnapshotWhenCompaction() + throws SQLException, MetadataException, StorageEngineException, InterruptedException, + DirectoryNotLegalException, IOException { + try (Connection connection = EnvFactory.getEnv().getConnection(); + Statement statement = connection.createStatement()) { + Map resultMap = new HashMap<>(); + statement.execute("set storage group to " + SG_NAME); + for (int i = 0; i < 10; ++i) { + for (int j = 0; j < 10; ++j) { + statement.execute( + String.format("insert into %s.d%d(time, s) values (%d, %d)", SG_NAME, i, j, j)); + } + statement.execute("flush"); + for (int j = 0; j < 10; ++j) { + statement.execute( + String.format("insert into %s.d%d(time, s) values (%d, %d)", SG_NAME, i, j, j + 1)); + } + statement.execute("flush"); + } + + ResultSet resultSet = statement.executeQuery("select ** from root"); + while (resultSet.next()) { + long time = resultSet.getLong("Time"); + for (int i = 0; i < 10; ++i) { + String measurment = SG_NAME + ".d" + i + ".s"; + int res = resultSet.getInt(SG_NAME + ".d" + i + ".s"); + resultMap.put(time + measurment, res); + } + } + + File snapshotDir = new File(TestConstant.OUTPUT_DATA_DIR, "snapshot"); + if (!snapshotDir.exists()) { + snapshotDir.mkdirs(); + } + IoTDBDescriptor.getInstance().getConfig().setEnableCrossSpaceCompaction(true); + statement.execute("merge"); + DataRegion region = StorageEngineV2.getInstance().getDataRegion(new DataRegionId(0)); + new SnapshotTaker(region).takeFullSnapshot(snapshotDir.getAbsolutePath(), true); + region.abortCompaction(); + StorageEngineV2.getInstance() + .setDataRegion( + new DataRegionId(0), + new SnapshotLoader(snapshotDir.getAbsolutePath(), SG_NAME, "0") + .loadSnapshotForStateMachine()); + ChunkCache.getInstance().clear(); + TimeSeriesMetadataCache.getInstance().clear(); + resultSet = statement.executeQuery("select ** from root"); + while (resultSet.next()) { + long time = resultSet.getLong("Time"); + for (int i = 0; i < 10; ++i) { + String measurment = SG_NAME + ".d" + i + ".s"; + int res = resultSet.getInt(SG_NAME + ".d" + i + ".s"); + Assert.assertEquals(resultMap.get(time + measurment).intValue(), res); + } + } + } + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/consensus/statemachine/DataRegionStateMachine.java b/server/src/main/java/org/apache/iotdb/db/consensus/statemachine/DataRegionStateMachine.java index 89110c470095..1decc0b84d37 100644 --- a/server/src/main/java/org/apache/iotdb/db/consensus/statemachine/DataRegionStateMachine.java +++ b/server/src/main/java/org/apache/iotdb/db/consensus/statemachine/DataRegionStateMachine.java @@ -20,8 +20,12 @@ package org.apache.iotdb.db.consensus.statemachine; import org.apache.iotdb.common.rpc.thrift.TSStatus; +import org.apache.iotdb.commons.consensus.DataRegionId; import org.apache.iotdb.consensus.common.DataSet; import org.apache.iotdb.db.consensus.statemachine.visitor.DataExecutionVisitor; +import org.apache.iotdb.db.engine.StorageEngineV2; +import org.apache.iotdb.db.engine.snapshot.SnapshotLoader; +import org.apache.iotdb.db.engine.snapshot.SnapshotTaker; import org.apache.iotdb.db.engine.storagegroup.DataRegion; import org.apache.iotdb.db.mpp.execution.fragment.FragmentInstanceManager; import org.apache.iotdb.db.mpp.plan.planner.plan.FragmentInstance; @@ -39,7 +43,7 @@ public class DataRegionStateMachine extends BaseStateMachine { private static final FragmentInstanceManager QUERY_INSTANCE_MANAGER = FragmentInstanceManager.getInstance(); - private final DataRegion region; + private DataRegion region; public DataRegionStateMachine(DataRegion region) { this.region = region; @@ -53,11 +57,39 @@ public void stop() {} @Override public boolean takeSnapshot(File snapshotDir) { - return false; + try { + return new SnapshotTaker(region).takeFullSnapshot(snapshotDir.getAbsolutePath(), true); + } catch (Exception e) { + logger.error( + "Exception occurs when taking snapshot for {}-{} in {}", + region.getLogicalStorageGroupName(), + region.getDataRegionId(), + snapshotDir, + e); + return false; + } } @Override - public void loadSnapshot(File latestSnapshotRootDir) {} + public void loadSnapshot(File latestSnapshotRootDir) { + DataRegion newRegion = + new SnapshotLoader( + latestSnapshotRootDir.getAbsolutePath(), + region.getLogicalStorageGroupName(), + region.getDataRegionId()) + .loadSnapshotForStateMachine(); + if (newRegion == null) { + logger.error("Fail to load snapshot from {}", latestSnapshotRootDir); + return; + } + this.region = newRegion; + try { + StorageEngineV2.getInstance() + .setDataRegion(new DataRegionId(Integer.parseInt(region.getDataRegionId())), region); + } catch (Exception e) { + logger.error("Exception occurs when replacing data region in storage engine.", e); + } + } @Override protected TSStatus write(FragmentInstance fragmentInstance) { diff --git a/server/src/main/java/org/apache/iotdb/db/engine/StorageEngineV2.java b/server/src/main/java/org/apache/iotdb/db/engine/StorageEngineV2.java index 30a716512f31..94ea120d2037 100644 --- a/server/src/main/java/org/apache/iotdb/db/engine/StorageEngineV2.java +++ b/server/src/main/java/org/apache/iotdb/db/engine/StorageEngineV2.java @@ -601,6 +601,19 @@ public DataRegion getDataRegion(DataRegionId regionId) { return dataRegionMap.get(regionId); } + public void setDataRegion(DataRegionId regionId, DataRegion newRegion) { + if (dataRegionMap.containsKey(regionId)) { + DataRegion oldRegion = dataRegionMap.get(regionId); + oldRegion.syncCloseAllWorkingTsFileProcessors(); + oldRegion.abortCompaction(); + } + dataRegionMap.put(regionId, newRegion); + } + + public TsFileFlushPolicy getFileFlushPolicy() { + return fileFlushPolicy; + } + static class InstanceHolder { private static final StorageEngineV2 INSTANCE = new StorageEngineV2(); diff --git a/server/src/main/java/org/apache/iotdb/db/engine/snapshot/SnapshotLoader.java b/server/src/main/java/org/apache/iotdb/db/engine/snapshot/SnapshotLoader.java new file mode 100644 index 000000000000..476a6455335e --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/engine/snapshot/SnapshotLoader.java @@ -0,0 +1,196 @@ +/* + * 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.iotdb.db.engine.snapshot; + +import org.apache.iotdb.commons.conf.IoTDBConstant; +import org.apache.iotdb.db.conf.IoTDBDescriptor; +import org.apache.iotdb.db.conf.directories.DirectoryManager; +import org.apache.iotdb.db.engine.StorageEngineV2; +import org.apache.iotdb.db.engine.storagegroup.DataRegion; +import org.apache.iotdb.db.exception.DiskSpaceInsufficientException; + +import org.apache.commons.io.FileUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class SnapshotLoader { + private Logger LOGGER = LoggerFactory.getLogger(SnapshotLoader.class); + private String storageGroupName; + private String snapshotPath; + private String dataRegionId; + + public SnapshotLoader(String snapshotPath, String storageGroupName, String dataRegionId) { + this.snapshotPath = snapshotPath; + this.storageGroupName = storageGroupName; + this.dataRegionId = dataRegionId; + } + + private DataRegion loadSnapshot() { + try { + return new DataRegion( + IoTDBDescriptor.getInstance().getConfig().getSystemDir() + + File.separator + + "storage_groups" + + File.separator + + storageGroupName, + dataRegionId, + StorageEngineV2.getInstance().getFileFlushPolicy(), + storageGroupName); + } catch (Exception e) { + LOGGER.error("Exception occurs while load snapshot from {}", snapshotPath, e); + return null; + } + } + + /** + * 1. Clear origin data 2. Move snapshot data to data dir 3. Load data region + * + * @return + */ + public DataRegion loadSnapshotForStateMachine() { + try { + deleteAllFilesInDataDirs(); + } catch (IOException e) { + return null; + } + + // move the snapshot data to data dir + String seqBaseDir = + IoTDBConstant.SEQUENCE_FLODER_NAME + + File.separator + + storageGroupName + + File.separator + + dataRegionId; + String unseqBaseDir = + IoTDBConstant.UNSEQUENCE_FLODER_NAME + + File.separator + + storageGroupName + + File.separator + + dataRegionId; + File sourceDataDir = new File(snapshotPath); + if (sourceDataDir.exists()) { + try { + createLinksFromSnapshotDirToDataDir(sourceDataDir, seqBaseDir, unseqBaseDir); + } catch (IOException | DiskSpaceInsufficientException e) { + LOGGER.error( + "Exception occurs when creating links from snapshot directory to data directory", e); + return null; + } + } + + return loadSnapshot(); + } + + private void deleteAllFilesInDataDirs() throws IOException { + String[] dataDirPaths = IoTDBDescriptor.getInstance().getConfig().getDataDirs(); + + // delete + List timePartitions = new ArrayList<>(); + for (String dataDirPath : dataDirPaths) { + File seqDataDirForThisRegion = + new File( + dataDirPath + + File.separator + + IoTDBConstant.SEQUENCE_FLODER_NAME + + File.separator + + storageGroupName + + File.separator + + dataRegionId); + if (seqDataDirForThisRegion.exists()) { + File[] files = seqDataDirForThisRegion.listFiles(); + if (files != null) { + timePartitions.addAll(Arrays.asList(files)); + } + } + + File unseqDataDirForThisRegion = + new File( + dataDirPath + + File.separator + + IoTDBConstant.UNSEQUENCE_FLODER_NAME + + File.separator + + storageGroupName + + File.separator + + dataRegionId); + + if (unseqDataDirForThisRegion.exists()) { + File[] files = unseqDataDirForThisRegion.listFiles(); + if (files != null) { + timePartitions.addAll(Arrays.asList(files)); + } + } + } + + try { + for (File timePartition : timePartitions) { + FileUtils.forceDelete(timePartition); + } + } catch (IOException e) { + LOGGER.error( + "Exception occurs when deleting time partition directory for {}-{}", + storageGroupName, + dataRegionId, + e); + throw e; + } + } + + private void createLinksFromSnapshotDirToDataDir( + File sourceDir, String seqBaseDir, String unseqBaseDir) + throws IOException, DiskSpaceInsufficientException { + File[] files = sourceDir.listFiles(); + if (files == null) { + return; + } + for (File sourceFile : files) { + String[] fileInfo = sourceFile.getName().split(SnapshotTaker.SNAPSHOT_FILE_INFO_SEP_STR); + if (fileInfo.length != 5) { + continue; + } + boolean seq = fileInfo[0].equals("seq"); + String timePartition = fileInfo[3]; + String fileName = fileInfo[4]; + String nextDataDir = + seq + ? DirectoryManager.getInstance().getNextFolderForSequenceFile() + : DirectoryManager.getInstance().getNextFolderForUnSequenceFile(); + File baseDir = new File(nextDataDir, seq ? seqBaseDir : unseqBaseDir); + File targetDirForThisTimePartition = new File(baseDir, timePartition); + if (!targetDirForThisTimePartition.exists() && !targetDirForThisTimePartition.mkdirs()) { + throw new IOException( + String.format("Failed to make directory %s", targetDirForThisTimePartition)); + } + + File targetFile = new File(targetDirForThisTimePartition, fileName); + try { + Files.createLink(targetFile.toPath(), sourceFile.toPath()); + } catch (IOException e) { + throw new IOException( + String.format("Failed to create hard link from %s to %s", sourceFile, targetFile), e); + } + } + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/engine/snapshot/SnapshotTaker.java b/server/src/main/java/org/apache/iotdb/db/engine/snapshot/SnapshotTaker.java new file mode 100644 index 000000000000..1842b1b83068 --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/engine/snapshot/SnapshotTaker.java @@ -0,0 +1,166 @@ +/* + * 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.iotdb.db.engine.snapshot; + +import org.apache.iotdb.commons.conf.IoTDBConstant; +import org.apache.iotdb.db.conf.IoTDBDescriptor; +import org.apache.iotdb.db.engine.compaction.log.CompactionLogger; +import org.apache.iotdb.db.engine.modification.ModificationFile; +import org.apache.iotdb.db.engine.snapshot.exception.DirectoryNotLegalException; +import org.apache.iotdb.db.engine.storagegroup.DataRegion; +import org.apache.iotdb.db.engine.storagegroup.TsFileResource; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.LinkedList; +import java.util.List; + +/** + * SnapshotTaker takes data snapshot for a DataRegion in one time. It does so by creating hard link + * for files or copying them. SnapshotTaker supports two different ways of snapshot: Full Snapshot + * and Incremental Snapshot. The former takes a snapshot for all files in an empty directory, and + * the latter takes a snapshot based on the snapshot that took before. + */ +public class SnapshotTaker { + private static final Logger LOGGER = LoggerFactory.getLogger(SnapshotTaker.class); + private final DataRegion dataRegion; + public static String SNAPSHOT_FILE_INFO_SEP_STR = "_"; + + public SnapshotTaker(DataRegion dataRegion) { + this.dataRegion = dataRegion; + } + + public boolean takeFullSnapshot(String snapshotDirPath, boolean flushBeforeSnapshot) + throws DirectoryNotLegalException, IOException { + File snapshotDir = new File(snapshotDirPath); + if (snapshotDir.exists() + && snapshotDir.listFiles() != null + && snapshotDir.listFiles().length > 0) { + // the directory should be empty or not exists + throw new DirectoryNotLegalException( + String.format("%s already exists and is not empty", snapshotDirPath)); + } + + if (!snapshotDir.exists() && !snapshotDir.mkdirs()) { + throw new IOException(String.format("Failed to create directory %s", snapshotDir)); + } + + if (flushBeforeSnapshot) { + dataRegion.syncCloseAllWorkingTsFileProcessors(); + } + + List timePartitions = dataRegion.getTimePartitions(); + for (Long timePartition : timePartitions) { + List seqDataDirs = getAllDataDirOfOnePartition(true, timePartition); + + try { + createFileSnapshot(seqDataDirs, snapshotDir, true, timePartition); + } catch (IOException e) { + LOGGER.error("Fail to create snapshot", e); + File[] files = snapshotDir.listFiles(); + if (files != null) { + for (File file : files) { + if (!file.delete()) { + LOGGER.error("Failed to delete link file {} after failing to create snapshot", file); + } + } + } + return false; + } + + List unseqDataDirs = getAllDataDirOfOnePartition(false, timePartition); + + try { + createFileSnapshot(unseqDataDirs, snapshotDir, false, timePartition); + } catch (IOException e) { + LOGGER.error("Fail to create snapshot", e); + return false; + } + } + + return true; + } + + private List getAllDataDirOfOnePartition(boolean sequence, long timePartition) { + String[] dataDirs = IoTDBDescriptor.getInstance().getConfig().getDataDirs(); + List resultDirs = new LinkedList<>(); + + for (String dataDir : dataDirs) { + resultDirs.add( + dataDir + + File.separator + + (sequence + ? IoTDBConstant.SEQUENCE_FLODER_NAME + : IoTDBConstant.UNSEQUENCE_FLODER_NAME) + + File.separator + + dataRegion.getLogicalStorageGroupName() + + File.separator + + dataRegion.getDataRegionId() + + File.separator + + timePartition + + File.separator); + } + return resultDirs; + } + + private void createFileSnapshot( + List sourceDirPaths, File targetDir, boolean sequence, long timePartition) + throws IOException { + for (String sourceDirPath : sourceDirPaths) { + File sourceDir = new File(sourceDirPath); + if (!sourceDir.exists()) { + continue; + } + // Collect TsFile, TsFileResource, Mods, CompactionMods + File[] files = + sourceDir.listFiles( + (dir, name) -> + name.endsWith(".tsfile") + || name.endsWith(TsFileResource.RESOURCE_SUFFIX) + || name.endsWith(ModificationFile.FILE_SUFFIX) + || name.endsWith(ModificationFile.COMPACTION_FILE_SUFFIX) + || name.endsWith(CompactionLogger.INNER_COMPACTION_LOG_NAME_SUFFIX) + || name.endsWith(CompactionLogger.CROSS_COMPACTION_LOG_NAME_SUFFIX) + || name.endsWith(IoTDBConstant.INNER_COMPACTION_TMP_FILE_SUFFIX) + || name.endsWith(IoTDBConstant.CROSS_COMPACTION_TMP_FILE_SUFFIX)); + if (files == null || files.length == 0) { + continue; + } + + for (File file : files) { + String newFileName = + (sequence ? "seq" : "unseq") + + SNAPSHOT_FILE_INFO_SEP_STR + + dataRegion.getLogicalStorageGroupName() + + SNAPSHOT_FILE_INFO_SEP_STR + + dataRegion.getDataRegionId() + + SNAPSHOT_FILE_INFO_SEP_STR + + timePartition + + SNAPSHOT_FILE_INFO_SEP_STR + + file.getName(); + File linkFile = new File(targetDir, newFileName); + Files.createLink(linkFile.toPath(), file.toPath()); + } + } + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/engine/snapshot/exception/DirectoryNotLegalException.java b/server/src/main/java/org/apache/iotdb/db/engine/snapshot/exception/DirectoryNotLegalException.java new file mode 100644 index 000000000000..bd4742d9e5e3 --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/engine/snapshot/exception/DirectoryNotLegalException.java @@ -0,0 +1,28 @@ +/* + * 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.iotdb.db.engine.snapshot.exception; + +import org.apache.iotdb.commons.exception.IoTDBException; +import org.apache.iotdb.rpc.TSStatusCode; + +public class DirectoryNotLegalException extends IoTDBException { + public DirectoryNotLegalException(String message) { + super(message, TSStatusCode.SNAPSHOT_DIR_NOT_LEGAL.getStatusCode()); + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/DataRegion.java b/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/DataRegion.java index 1fbd2ab5e0a4..9fbc91165539 100755 --- a/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/DataRegion.java +++ b/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/DataRegion.java @@ -3486,6 +3486,10 @@ void call(TsFileResource oldTsFileResource, List newTsFileResour throws WriteProcessException; } + public List getTimePartitions() { + return new ArrayList<>(partitionMaxFileVersions.keySet()); + } + public String getInsertWriteLockHolder() { return insertWriteLockHolder; } diff --git a/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/TsFileNameGenerator.java b/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/TsFileNameGenerator.java index b185838b320a..946d8274610a 100644 --- a/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/TsFileNameGenerator.java +++ b/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/TsFileNameGenerator.java @@ -71,7 +71,7 @@ public static String generateNewTsFilePathWithMkdir( time, version, innerSpaceCompactionCount, crossSpaceCompactionCount); } - private static String generateTsFileDir( + public static String generateTsFileDir( boolean sequence, String logicalStorageGroup, String virtualStorageGroup, diff --git a/service-rpc/src/main/java/org/apache/iotdb/rpc/TSStatusCode.java b/service-rpc/src/main/java/org/apache/iotdb/rpc/TSStatusCode.java index f612f6732b07..bb037da449be 100644 --- a/service-rpc/src/main/java/org/apache/iotdb/rpc/TSStatusCode.java +++ b/service-rpc/src/main/java/org/apache/iotdb/rpc/TSStatusCode.java @@ -83,6 +83,7 @@ public enum TSStatusCode { WRITE_PROCESS_ERROR(412), WRITE_PROCESS_REJECT(413), QUERY_ID_NOT_EXIST(414), + SNAPSHOT_DIR_NOT_LEGAL(415), UNSUPPORTED_INDEX_FUNC_ERROR(421), UNSUPPORTED_INDEX_TYPE_ERROR(422), From eaeb42bee44c8cc9682a7adb5315842c8243ec20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=B2=9B=E8=BE=B0?= <45144903+choubenson@users.noreply.github.com> Date: Thu, 19 May 2022 10:10:29 +0800 Subject: [PATCH 047/436] [IOTDB-3163] Refactor compaction memory estimator frame and add new estimator of RewriteCrossCompaction (#5868) --- .../iotdb/tsfile/TsFileSequenceRead.java | 2 +- .../compaction/CompactionScheduler.java | 4 +- .../db/engine/compaction/CompactionUtils.java | 4 +- .../InnerSequenceCompactionSelector.java | 8 +- .../InnerUnsequenceCompactionSelector.java | 8 +- .../cross/AbstractCrossSpaceEstimator.java | 39 ++++ .../rewrite/CrossSpaceCompactionResource.java | 89 ++++++++ .../RewriteCrossSpaceCompactionResource.java | 206 ------------------ .../RewriteCrossSpaceCompactionSelector.java | 17 +- .../RewriteCompactionFileSelector.java | 195 ++++------------- .../utils/AbstractCompactionEstimator.java | 63 ++++++ .../IFileQueryMemMeasurement.java | 2 +- .../utils/InplaceCompactionEstimator.java | 173 +++++++++++++++ .../ReadPointCrossCompactionEstimator.java | 172 +++++++++++++++ .../inner/AbstractInnerSpaceEstimator.java | 39 ++++ .../SizeTieredCompactionSelector.java | 9 +- .../compaction/task/ICompactionSelector.java | 20 ++ .../iotdb/db/qp/executor/PlanExecutor.java | 2 +- .../iotdb/db/tools/TsFileSketchTool.java | 6 +- .../iotdb/db/utils/FileLoaderUtils.java | 2 +- .../org/apache/iotdb/db/utils/MergeUtils.java | 69 ------ .../cross/CrossSpaceCompactionTest.java | 17 +- .../compaction/cross/MergeUpgradeTest.java | 6 +- .../RewriteCompactionFileSelectorTest.java | 81 +++---- .../tsfile/read/TsFileSequenceReader.java | 15 +- .../v2/read/TsFileSequenceReaderForV2.java | 3 +- .../write/MetadataIndexConstructorTest.java | 2 +- .../tsfile/write/TsFileIOWriterTest.java | 2 +- 28 files changed, 716 insertions(+), 539 deletions(-) create mode 100644 server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/AbstractCrossSpaceEstimator.java create mode 100644 server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/rewrite/CrossSpaceCompactionResource.java delete mode 100644 server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/rewrite/RewriteCrossSpaceCompactionResource.java create mode 100644 server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/utils/AbstractCompactionEstimator.java rename server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/{rewrite/selector => utils}/IFileQueryMemMeasurement.java (94%) create mode 100644 server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/utils/InplaceCompactionEstimator.java create mode 100644 server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/utils/ReadPointCrossCompactionEstimator.java create mode 100644 server/src/main/java/org/apache/iotdb/db/engine/compaction/inner/AbstractInnerSpaceEstimator.java delete mode 100644 server/src/main/java/org/apache/iotdb/db/utils/MergeUtils.java diff --git a/example/tsfile/src/main/java/org/apache/iotdb/tsfile/TsFileSequenceRead.java b/example/tsfile/src/main/java/org/apache/iotdb/tsfile/TsFileSequenceRead.java index 6d2c2ff89178..aa946f67b7d5 100644 --- a/example/tsfile/src/main/java/org/apache/iotdb/tsfile/TsFileSequenceRead.java +++ b/example/tsfile/src/main/java/org/apache/iotdb/tsfile/TsFileSequenceRead.java @@ -63,7 +63,7 @@ public static void main(String[] args) throws IOException { System.out.println("file magic head: " + reader.readHeadMagic()); System.out.println("file magic tail: " + reader.readTailMagic()); System.out.println("Level 1 metadata position: " + reader.getFileMetadataPos()); - System.out.println("Level 1 metadata size: " + reader.getFileMetadataSize()); + System.out.println("Level 1 metadata size: " + reader.getTsFileMetadataSize()); // Sequential reading of one ChunkGroup now follows this order: // first the CHUNK_GROUP_HEADER, then SeriesChunks (headers and data) in one ChunkGroup // Because we do not know how many chunks a ChunkGroup may have, we should read one byte (the diff --git a/server/src/main/java/org/apache/iotdb/db/engine/compaction/CompactionScheduler.java b/server/src/main/java/org/apache/iotdb/db/engine/compaction/CompactionScheduler.java index 91cd7a7d9fd3..cffa50884a3a 100644 --- a/server/src/main/java/org/apache/iotdb/db/engine/compaction/CompactionScheduler.java +++ b/server/src/main/java/org/apache/iotdb/db/engine/compaction/CompactionScheduler.java @@ -96,12 +96,12 @@ public static void tryToSubmitInnerSpaceCompactionTask( innerSpaceCompactionSelector = config .getInnerSequenceCompactionSelector() - .createInstance(logicalStorageGroupName, dataRegionId, timePartition, tsFileManager); + .createInstance(logicalStorageGroupName, dataRegionId, timePartition); } else { innerSpaceCompactionSelector = config .getInnerUnsequenceCompactionSelector() - .createInstance(logicalStorageGroupName, dataRegionId, timePartition, tsFileManager); + .createInstance(logicalStorageGroupName, dataRegionId, timePartition); } List> taskList = innerSpaceCompactionSelector.selectInnerSpaceTask( diff --git a/server/src/main/java/org/apache/iotdb/db/engine/compaction/CompactionUtils.java b/server/src/main/java/org/apache/iotdb/db/engine/compaction/CompactionUtils.java index ac3d9bcfaec9..096f03cbf46d 100644 --- a/server/src/main/java/org/apache/iotdb/db/engine/compaction/CompactionUtils.java +++ b/server/src/main/java/org/apache/iotdb/db/engine/compaction/CompactionUtils.java @@ -21,7 +21,7 @@ import org.apache.iotdb.commons.conf.IoTDBConstant; import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.engine.compaction.constant.CrossCompactionSelector; -import org.apache.iotdb.db.engine.compaction.cross.rewrite.RewriteCrossSpaceCompactionResource; +import org.apache.iotdb.db.engine.compaction.cross.rewrite.CrossSpaceCompactionResource; import org.apache.iotdb.db.engine.compaction.cross.rewrite.selector.ICrossSpaceMergeFileSelector; import org.apache.iotdb.db.engine.compaction.cross.rewrite.selector.RewriteCompactionFileSelector; import org.apache.iotdb.db.engine.modification.Modification; @@ -230,7 +230,7 @@ public static void deleteModificationForSourceFile( } public static ICrossSpaceMergeFileSelector getCrossSpaceFileSelector( - long budget, RewriteCrossSpaceCompactionResource resource) { + long budget, CrossSpaceCompactionResource resource) { CrossCompactionSelector strategy = IoTDBDescriptor.getInstance().getConfig().getCrossCompactionSelector(); switch (strategy) { diff --git a/server/src/main/java/org/apache/iotdb/db/engine/compaction/constant/InnerSequenceCompactionSelector.java b/server/src/main/java/org/apache/iotdb/db/engine/compaction/constant/InnerSequenceCompactionSelector.java index 0ab9885fc571..a40fefa08d13 100644 --- a/server/src/main/java/org/apache/iotdb/db/engine/compaction/constant/InnerSequenceCompactionSelector.java +++ b/server/src/main/java/org/apache/iotdb/db/engine/compaction/constant/InnerSequenceCompactionSelector.java @@ -21,7 +21,6 @@ import org.apache.iotdb.db.engine.compaction.inner.IInnerSeqSpaceSelector; import org.apache.iotdb.db.engine.compaction.inner.sizetiered.SizeTieredCompactionSelector; -import org.apache.iotdb.db.engine.storagegroup.TsFileManager; public enum InnerSequenceCompactionSelector { SIZE_TIERED; @@ -34,15 +33,12 @@ public static InnerSequenceCompactionSelector getInnerSequenceCompactionSelector } public IInnerSeqSpaceSelector createInstance( - String logicalStorageGroupName, - String virtualStorageGroupName, - long timePartition, - TsFileManager tsFileManager) { + String logicalStorageGroupName, String virtualStorageGroupName, long timePartition) { switch (this) { case SIZE_TIERED: default: return new SizeTieredCompactionSelector( - logicalStorageGroupName, virtualStorageGroupName, timePartition, tsFileManager, true); + logicalStorageGroupName, virtualStorageGroupName, timePartition, true); } } } diff --git a/server/src/main/java/org/apache/iotdb/db/engine/compaction/constant/InnerUnsequenceCompactionSelector.java b/server/src/main/java/org/apache/iotdb/db/engine/compaction/constant/InnerUnsequenceCompactionSelector.java index a1ec453317c9..5c5698f51d2c 100644 --- a/server/src/main/java/org/apache/iotdb/db/engine/compaction/constant/InnerUnsequenceCompactionSelector.java +++ b/server/src/main/java/org/apache/iotdb/db/engine/compaction/constant/InnerUnsequenceCompactionSelector.java @@ -20,7 +20,6 @@ import org.apache.iotdb.db.engine.compaction.inner.IInnerUnseqSpaceSelector; import org.apache.iotdb.db.engine.compaction.inner.sizetiered.SizeTieredCompactionSelector; -import org.apache.iotdb.db.engine.storagegroup.TsFileManager; public enum InnerUnsequenceCompactionSelector { SIZE_TIERED; @@ -34,15 +33,12 @@ public static InnerUnsequenceCompactionSelector getInnerUnsequenceCompactionSele } public IInnerUnseqSpaceSelector createInstance( - String logicalStorageGroupName, - String virtualStorageGroupName, - long timePartition, - TsFileManager tsFileManager) { + String logicalStorageGroupName, String virtualStorageGroupName, long timePartition) { switch (this) { case SIZE_TIERED: default: return new SizeTieredCompactionSelector( - logicalStorageGroupName, virtualStorageGroupName, timePartition, tsFileManager, false); + logicalStorageGroupName, virtualStorageGroupName, timePartition, false); } } } diff --git a/server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/AbstractCrossSpaceEstimator.java b/server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/AbstractCrossSpaceEstimator.java new file mode 100644 index 000000000000..9c6f3e8fdf39 --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/AbstractCrossSpaceEstimator.java @@ -0,0 +1,39 @@ +/* + * 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.iotdb.db.engine.compaction.cross; + +import org.apache.iotdb.db.engine.compaction.cross.utils.AbstractCompactionEstimator; +import org.apache.iotdb.db.engine.storagegroup.TsFileResource; + +import java.io.IOException; +import java.util.List; + +/** + * Estimate the memory cost of one cross space compaction task with specific source files based on + * its corresponding implementation. + */ +public abstract class AbstractCrossSpaceEstimator extends AbstractCompactionEstimator { + public abstract long estimateCrossCompactionMemory( + List seqResources, TsFileResource unseqResource) throws IOException; + + public long estimateInnerCompactionMemory(List resources) { + throw new RuntimeException( + "This kind of estimator cannot be used to estimate inner space compaction task"); + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/rewrite/CrossSpaceCompactionResource.java b/server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/rewrite/CrossSpaceCompactionResource.java new file mode 100644 index 000000000000..a99133636ae7 --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/rewrite/CrossSpaceCompactionResource.java @@ -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.iotdb.db.engine.compaction.cross.rewrite; + +import org.apache.iotdb.db.engine.storagegroup.TsFileResource; +import org.apache.iotdb.db.engine.storagegroup.TsFileResourceStatus; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +/** + * CrossSpaceCompactionResource manages files and caches of readers to avoid unnecessary object + * creations and file openings. + */ +public class CrossSpaceCompactionResource { + private List seqFiles; + private List unseqFiles = new ArrayList<>(); + + private long ttlLowerBound = Long.MIN_VALUE; + + public CrossSpaceCompactionResource( + List seqFiles, List unseqFiles) { + this.seqFiles = seqFiles.stream().filter(this::filterSeqResource).collect(Collectors.toList()); + filterUnseqResource(unseqFiles); + } + + /** Fitler the seq files into the compaction. Seq files should be not deleted or over ttl. */ + private boolean filterSeqResource(TsFileResource res) { + return !res.isDeleted() && res.stillLives(ttlLowerBound); + } + + /** + * Filter the unseq files into the compaction. Unseq files should be not deleted or over ttl. To + * ensure that the compaction is correct, return as soon as it encounters the file being compacted + * or compaction candidate. Therefore, a cross space compaction can only be performed serially + * under a time partition in a VSG. + */ + private void filterUnseqResource(List unseqResources) { + for (TsFileResource resource : unseqResources) { + if (resource.getStatus() != TsFileResourceStatus.CLOSED) { + return; + } else if (!resource.isDeleted() && resource.stillLives(ttlLowerBound)) { + this.unseqFiles.add(resource); + } + } + } + + public CrossSpaceCompactionResource( + Collection seqFiles, List unseqFiles, long ttlLowerBound) { + this.ttlLowerBound = ttlLowerBound; + this.seqFiles = seqFiles.stream().filter(this::filterSeqResource).collect(Collectors.toList()); + filterUnseqResource(unseqFiles); + } + + public List getSeqFiles() { + return seqFiles; + } + + public void setSeqFiles(List seqFiles) { + this.seqFiles = seqFiles; + } + + public List getUnseqFiles() { + return unseqFiles; + } + + public void setUnseqFiles(List unseqFiles) { + this.unseqFiles = unseqFiles; + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/rewrite/RewriteCrossSpaceCompactionResource.java b/server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/rewrite/RewriteCrossSpaceCompactionResource.java deleted file mode 100644 index 5a06584afdb7..000000000000 --- a/server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/rewrite/RewriteCrossSpaceCompactionResource.java +++ /dev/null @@ -1,206 +0,0 @@ -/* - * 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.iotdb.db.engine.compaction.cross.rewrite; - -import org.apache.iotdb.commons.path.PartialPath; -import org.apache.iotdb.db.engine.modification.Modification; -import org.apache.iotdb.db.engine.storagegroup.TsFileResource; -import org.apache.iotdb.db.engine.storagegroup.TsFileResourceStatus; -import org.apache.iotdb.tsfile.read.TsFileSequenceReader; -import org.apache.iotdb.tsfile.utils.Pair; -import org.apache.iotdb.tsfile.write.chunk.ChunkWriterImpl; -import org.apache.iotdb.tsfile.write.schema.IMeasurementSchema; -import org.apache.iotdb.tsfile.write.writer.RestorableTsFileIOWriter; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; - -/** - * CrossSpaceMergeResource manages files and caches of readers, writers, MeasurementSchemas and - * modifications to avoid unnecessary object creations and file openings. - */ -public class RewriteCrossSpaceCompactionResource { - - private List seqFiles; - private List unseqFiles = new ArrayList<>(); - - private Map fileReaderCache = new HashMap<>(); - private Map fileWriterCache = new HashMap<>(); - private Map> modificationCache = new HashMap<>(); - private Map>> startEndTimeCache = - new HashMap<>(); // pair - private Map chunkWriterCache = new ConcurrentHashMap<>(); - - private long ttlLowerBound = Long.MIN_VALUE; - - private boolean cacheDeviceMeta = false; - - public RewriteCrossSpaceCompactionResource( - List seqFiles, List unseqFiles) { - this.seqFiles = seqFiles.stream().filter(this::filterSeqResource).collect(Collectors.toList()); - filterUnseqResource(unseqFiles); - } - - /** Fitler the seq files into the compaction. Seq files should be not deleted or over ttl. */ - private boolean filterSeqResource(TsFileResource res) { - return !res.isDeleted() && res.stillLives(ttlLowerBound); - } - - /** - * Filter the unseq files into the compaction. Unseq files should be not deleted or over ttl. To - * ensure that the compaction is correct, return as soon as it encounters the file being compacted - * or compaction candidate. Therefore, a cross space compaction can only be performed serially - * under a time partition in a VSG. - */ - private void filterUnseqResource(List unseqResources) { - for (TsFileResource resource : unseqResources) { - if (resource.getStatus() != TsFileResourceStatus.CLOSED) { - return; - } else if (!resource.isDeleted() && resource.stillLives(ttlLowerBound)) { - this.unseqFiles.add(resource); - } - } - } - - public RewriteCrossSpaceCompactionResource( - Collection seqFiles, List unseqFiles, long ttlLowerBound) { - this.ttlLowerBound = ttlLowerBound; - this.seqFiles = seqFiles.stream().filter(this::filterSeqResource).collect(Collectors.toList()); - filterUnseqResource(unseqFiles); - } - - public void clear() throws IOException { - for (TsFileSequenceReader sequenceReader : fileReaderCache.values()) { - sequenceReader.close(); - } - for (RestorableTsFileIOWriter writer : fileWriterCache.values()) { - writer.close(); - } - - fileReaderCache.clear(); - fileWriterCache.clear(); - modificationCache.clear(); - chunkWriterCache.clear(); - } - - /** - * Construct the a new or get an existing TsFileSequenceReader of a TsFile. - * - * @return a TsFileSequenceReader - */ - public TsFileSequenceReader getFileReader(TsFileResource tsFileResource) throws IOException { - TsFileSequenceReader reader = fileReaderCache.get(tsFileResource); - if (reader == null) { - reader = new TsFileSequenceReader(tsFileResource.getTsFilePath(), true, cacheDeviceMeta); - fileReaderCache.put(tsFileResource, reader); - } - return reader; - } - - /** - * Get the modifications of a timeseries in the ModificationFile of a TsFile. - * - * @param path name of the time series - */ - public List getModifications(TsFileResource tsFileResource, PartialPath path) { - // copy from TsFileResource so queries are not affected - List modifications = - modificationCache.computeIfAbsent( - tsFileResource, resource -> new LinkedList<>(resource.getModFile().getModifications())); - List pathModifications = new ArrayList<>(); - Iterator modificationIterator = modifications.iterator(); - while (modificationIterator.hasNext()) { - Modification modification = modificationIterator.next(); - if (modification.getPath().matchFullPath(path)) { - pathModifications.add(modification); - } - } - return pathModifications; - } - - public List getSeqFiles() { - return seqFiles; - } - - public void setSeqFiles(List seqFiles) { - this.seqFiles = seqFiles; - } - - public List getUnseqFiles() { - return unseqFiles; - } - - public void setUnseqFiles(List unseqFiles) { - this.unseqFiles = unseqFiles; - } - - public void removeOutdatedSeqReaders() throws IOException { - Iterator> entryIterator = - fileReaderCache.entrySet().iterator(); - HashSet fileSet = new HashSet<>(seqFiles); - while (entryIterator.hasNext()) { - Entry entry = entryIterator.next(); - TsFileResource tsFile = entry.getKey(); - if (!fileSet.contains(tsFile)) { - TsFileSequenceReader reader = entry.getValue(); - reader.close(); - entryIterator.remove(); - } - } - } - - public void setCacheDeviceMeta(boolean cacheDeviceMeta) { - this.cacheDeviceMeta = cacheDeviceMeta; - } - - public void updateStartTime(TsFileResource tsFileResource, String device, long startTime) { - Map> deviceStartEndTimePairMap = - startEndTimeCache.getOrDefault(tsFileResource, new HashMap<>()); - Pair startEndTimePair = - deviceStartEndTimePairMap.getOrDefault(device, new Pair<>(Long.MAX_VALUE, Long.MIN_VALUE)); - long newStartTime = startEndTimePair.left > startTime ? startTime : startEndTimePair.left; - deviceStartEndTimePairMap.put(device, new Pair<>(newStartTime, startEndTimePair.right)); - startEndTimeCache.put(tsFileResource, deviceStartEndTimePairMap); - } - - public void updateEndTime(TsFileResource tsFileResource, String device, long endTime) { - Map> deviceStartEndTimePairMap = - startEndTimeCache.getOrDefault(tsFileResource, new HashMap<>()); - Pair startEndTimePair = - deviceStartEndTimePairMap.getOrDefault(device, new Pair<>(Long.MAX_VALUE, Long.MIN_VALUE)); - long newEndTime = startEndTimePair.right < endTime ? endTime : startEndTimePair.right; - deviceStartEndTimePairMap.put(device, new Pair<>(startEndTimePair.left, newEndTime)); - startEndTimeCache.put(tsFileResource, deviceStartEndTimePairMap); - } - - public Map> getStartEndTime(TsFileResource tsFileResource) { - return startEndTimeCache.getOrDefault(tsFileResource, new HashMap<>()); - } -} diff --git a/server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/rewrite/RewriteCrossSpaceCompactionSelector.java b/server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/rewrite/RewriteCrossSpaceCompactionSelector.java index 3950afc75ce9..efae44c1a535 100644 --- a/server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/rewrite/RewriteCrossSpaceCompactionSelector.java +++ b/server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/rewrite/RewriteCrossSpaceCompactionSelector.java @@ -33,7 +33,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; @@ -89,17 +88,15 @@ public List selectCrossSpaceTask( } long budget = config.getCrossCompactionMemoryBudget(); long timeLowerBound = System.currentTimeMillis() - Long.MAX_VALUE; - RewriteCrossSpaceCompactionResource mergeResource = - new RewriteCrossSpaceCompactionResource(seqFileList, unSeqFileList, timeLowerBound); + CrossSpaceCompactionResource compactionResource = + new CrossSpaceCompactionResource(seqFileList, unSeqFileList, timeLowerBound); ICrossSpaceMergeFileSelector fileSelector = - CompactionUtils.getCrossSpaceFileSelector(budget, mergeResource); + CompactionUtils.getCrossSpaceFileSelector(budget, compactionResource); try { List[] mergeFiles = fileSelector.select(); - // avoid pending tasks holds the metadata and streams - mergeResource.clear(); if (mergeFiles.length == 0) { - if (mergeResource.getUnseqFiles().size() > 0) { + if (compactionResource.getUnseqFiles().size() > 0) { // still have unseq files but cannot be selected LOGGER.warn( "{} cannot select merge candidates under the budget {}", @@ -117,12 +114,12 @@ public List selectCrossSpaceTask( LOGGER.info( "{} [Compaction] submit a task with {} sequence file and {} unseq files", logicalStorageGroupName + "-" + dataRegionId, - mergeResource.getSeqFiles().size(), - mergeResource.getUnseqFiles().size()); + compactionResource.getSeqFiles().size(), + compactionResource.getUnseqFiles().size()); return Collections.singletonList(new Pair<>(mergeFiles[0], mergeFiles[1])); } - } catch (MergeException | IOException e) { + } catch (MergeException e) { LOGGER.error("{} cannot select file for cross space compaction", logicalStorageGroupName, e); } return Collections.emptyList(); diff --git a/server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/rewrite/selector/RewriteCompactionFileSelector.java b/server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/rewrite/selector/RewriteCompactionFileSelector.java index bba5badfb556..694cc84c1adc 100644 --- a/server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/rewrite/selector/RewriteCompactionFileSelector.java +++ b/server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/rewrite/selector/RewriteCompactionFileSelector.java @@ -19,11 +19,13 @@ package org.apache.iotdb.db.engine.compaction.cross.rewrite.selector; +import org.apache.iotdb.commons.conf.IoTDBConstant; import org.apache.iotdb.db.conf.IoTDBDescriptor; -import org.apache.iotdb.db.engine.compaction.cross.rewrite.RewriteCrossSpaceCompactionResource; +import org.apache.iotdb.db.engine.compaction.cross.rewrite.CrossSpaceCompactionResource; +import org.apache.iotdb.db.engine.compaction.cross.utils.AbstractCompactionEstimator; +import org.apache.iotdb.db.engine.compaction.task.ICompactionSelector; import org.apache.iotdb.db.engine.storagegroup.TsFileResource; import org.apache.iotdb.db.exception.MergeException; -import org.apache.iotdb.db.utils.MergeUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,10 +33,8 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Map; /** * MaxFileMergeFileSelector selects the most files from given seqFiles and unseqFiles which can be @@ -42,40 +42,33 @@ * queried at the same time is 1 to maximize the number of file merged. */ public class RewriteCompactionFileSelector implements ICrossSpaceMergeFileSelector { + private static final Logger logger = + LoggerFactory.getLogger(IoTDBConstant.COMPACTION_LOGGER_NAME); - private static final Logger logger = LoggerFactory.getLogger(RewriteCompactionFileSelector.class); - private static final String LOG_FILE_COST = "Memory cost of file {} is {}"; + private final CrossSpaceCompactionResource resource; - RewriteCrossSpaceCompactionResource resource; + private long totalCost; + private final long memoryBudget; + private final int maxCrossCompactionFileNum; - long totalCost; - private long memoryBudget; - private long maxSeqFileCost; - private int maxCrossCompactionFileNum; - - // the number of timeseries being queried at the same time - int concurrentMergeNum = 1; - - /** Total metadata size of each file. */ - private Map fileMetaSizeMap = new HashMap<>(); - /** Maximum memory cost of querying a timeseries in each file. */ - private Map maxSeriesQueryCostMap = new HashMap<>(); - - List selectedUnseqFiles; - List selectedSeqFiles; + private List selectedUnseqFiles; + private List selectedSeqFiles; private Collection tmpSelectedSeqFiles; - private long tempMaxSeqFileCost; private boolean[] seqSelected; private int seqSelectedNum; - public RewriteCompactionFileSelector( - RewriteCrossSpaceCompactionResource resource, long memoryBudget) { + private AbstractCompactionEstimator compactionEstimator; + + public RewriteCompactionFileSelector(CrossSpaceCompactionResource resource, long memoryBudget) { this.resource = resource; this.memoryBudget = memoryBudget; this.maxCrossCompactionFileNum = IoTDBDescriptor.getInstance().getConfig().getMaxCrossCompactionCandidateFileNum(); + this.compactionEstimator = + ICompactionSelector.getCompactionEstimator( + IoTDBDescriptor.getInstance().getConfig().getCrossCompactionPerformer(), false); } /** @@ -108,29 +101,28 @@ public List[] select() throws MergeException { "Selecting merge candidates from {} seqFile, {} unseqFiles", resource.getSeqFiles().size(), resource.getUnseqFiles().size()); - select(false); - if (selectedUnseqFiles.isEmpty()) { - select(true); - } - resource.setSeqFiles(selectedSeqFiles); - resource.setUnseqFiles(selectedUnseqFiles); - resource.removeOutdatedSeqReaders(); + selectSourceFiles(); if (selectedUnseqFiles.isEmpty()) { logger.debug("No merge candidates are found"); return new List[0]; } } catch (IOException e) { throw new MergeException(e); + } finally { + try { + compactionEstimator.clear(); + } catch (IOException e) { + throw new MergeException(e); + } } - if (logger.isInfoEnabled()) { - logger.info( - "Selected merge candidates, {} seqFiles, {} unseqFiles, total memory cost {}, " - + "time consumption {}ms", - selectedSeqFiles.size(), - selectedUnseqFiles.size(), - totalCost, - System.currentTimeMillis() - startTime); - } + logger.info( + "Selected merge candidates, {} seqFiles, {} unseqFiles, total memory cost {}, " + + "time consumption {}ms", + selectedSeqFiles.size(), + selectedUnseqFiles.size(), + totalCost, + System.currentTimeMillis() - startTime); + return new List[] {selectedSeqFiles, selectedUnseqFiles}; } @@ -141,18 +133,13 @@ public List[] select() throws MergeException { * that may be added by compacting them (preferably using the loop estimate), and if it does not * exceed the memory overhead preset by the system for the compaction thread, put them into the * selectedSeqFiles and selectedUnseqFiles. - * - * @param useTightBound whether is tight estimate or loop estimate - * @throws IOException */ - void select(boolean useTightBound) throws IOException { + void selectSourceFiles() throws IOException { tmpSelectedSeqFiles = new HashSet<>(); seqSelected = new boolean[resource.getSeqFiles().size()]; seqSelectedNum = 0; selectedSeqFiles = new ArrayList<>(); selectedUnseqFiles = new ArrayList<>(); - maxSeqFileCost = 0; - tempMaxSeqFileCost = 0; totalCost = 0; @@ -177,11 +164,12 @@ void select(boolean useTightBound) throws IOException { break; } - tempMaxSeqFileCost = maxSeqFileCost; + List tmpSelectedSeqFileResources = new ArrayList<>(); + for (int seqIndex : tmpSelectedSeqFiles) { + tmpSelectedSeqFileResources.add(resource.getSeqFiles().get(seqIndex)); + } long newCost = - useTightBound - ? calculateTightMemoryCost(unseqFile, tmpSelectedSeqFiles, startTime, timeLimit) - : calculateLooseMemoryCost(unseqFile, tmpSelectedSeqFiles, startTime, timeLimit); + compactionEstimator.estimateCrossCompactionMemory(tmpSelectedSeqFileResources, unseqFile); if (!updateSelectedFiles(newCost, unseqFile)) { // older unseq files must be merged before newer ones break; @@ -204,7 +192,6 @@ private boolean updateSelectedFiles(long newCost, TsFileResource unseqFile) { <= maxCrossCompactionFileNum && totalCost + newCost < memoryBudget)) { selectedUnseqFiles.add(unseqFile); - maxSeqFileCost = tempMaxSeqFileCost; for (Integer seqIdx : tmpSelectedSeqFiles) { if (!seqSelected[seqIdx]) { @@ -289,108 +276,4 @@ private void selectOverlappedSeqFiles(TsFileResource unseqFile) { } } } - - private long calculateMemoryCost( - TsFileResource tmpSelectedUnseqFile, - Collection tmpSelectedSeqFiles, - IFileQueryMemMeasurement unseqMeasurement, - IFileQueryMemMeasurement seqMeasurement, - long startTime, - long timeLimit) - throws IOException { - long cost = 0; - Long fileCost = unseqMeasurement.measure(tmpSelectedUnseqFile); - cost += fileCost; - - for (Integer seqFileIdx : tmpSelectedSeqFiles) { - TsFileResource seqFile = resource.getSeqFiles().get(seqFileIdx); - fileCost = seqMeasurement.measure(seqFile); - if (fileCost > tempMaxSeqFileCost) { - // only one file will be read at the same time, so only the largest one is recorded here - cost -= tempMaxSeqFileCost; - cost += fileCost; - tempMaxSeqFileCost = fileCost; - } - // but writing data into a new file may generate the same amount of metadata in memory - cost += calculateMetadataSize(seqFile); - long timeConsumption = System.currentTimeMillis() - startTime; - if (timeConsumption > timeLimit) { - return Long.MAX_VALUE; - } - } - return cost; - } - - private long calculateLooseMemoryCost( - TsFileResource tmpSelectedUnseqFile, - Collection tmpSelectedSeqFiles, - long startTime, - long timeLimit) - throws IOException { - return calculateMemoryCost( - tmpSelectedUnseqFile, - tmpSelectedSeqFiles, - TsFileResource::getTsFileSize, - this::calculateMetadataSize, - startTime, - timeLimit); - } - - private long calculateTightMemoryCost( - TsFileResource tmpSelectedUnseqFile, - Collection tmpSelectedSeqFiles, - long startTime, - long timeLimit) - throws IOException { - return calculateMemoryCost( - tmpSelectedUnseqFile, - tmpSelectedSeqFiles, - this::calculateTightUnseqMemoryCost, - this::calculateTightSeqMemoryCost, - startTime, - timeLimit); - } - - private long calculateMetadataSize(TsFileResource seqFile) throws IOException { - Long cost = fileMetaSizeMap.get(seqFile); - if (cost == null) { - cost = MergeUtils.getFileMetaSize(seqFile, resource.getFileReader(seqFile)); - fileMetaSizeMap.put(seqFile, cost); - logger.debug(LOG_FILE_COST, seqFile, cost); - } - return cost; - } - - private long calculateTightFileMemoryCost( - TsFileResource seqFile, IFileQueryMemMeasurement measurement) throws IOException { - Long cost = maxSeriesQueryCostMap.get(seqFile); - if (cost == null) { - long[] chunkNums = - MergeUtils.findTotalAndLargestSeriesChunkNum(seqFile, resource.getFileReader(seqFile)); - long totalChunkNum = chunkNums[0]; - long maxChunkNum = chunkNums[1]; - cost = measurement.measure(seqFile) * maxChunkNum / totalChunkNum; - maxSeriesQueryCostMap.put(seqFile, cost); - logger.debug(LOG_FILE_COST, seqFile, cost); - } - return cost; - } - - // this method traverses all ChunkMetadata to find out which series has the most chunks and uses - // its proportion to all series to get a maximum estimation - private long calculateTightSeqMemoryCost(TsFileResource seqFile) throws IOException { - long singleSeriesCost = calculateTightFileMemoryCost(seqFile, this::calculateMetadataSize); - long multiSeriesCost = concurrentMergeNum * singleSeriesCost; - long maxCost = calculateMetadataSize(seqFile); - return Math.min(multiSeriesCost, maxCost); - } - - // this method traverses all ChunkMetadata to find out which series has the most chunks and uses - // its proportion among all series to get a maximum estimation - private long calculateTightUnseqMemoryCost(TsFileResource unseqFile) throws IOException { - long singleSeriesCost = calculateTightFileMemoryCost(unseqFile, TsFileResource::getTsFileSize); - long multiSeriesCost = concurrentMergeNum * singleSeriesCost; - long maxCost = unseqFile.getTsFileSize(); - return Math.min(multiSeriesCost, maxCost); - } } diff --git a/server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/utils/AbstractCompactionEstimator.java b/server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/utils/AbstractCompactionEstimator.java new file mode 100644 index 000000000000..0eb1f408ccfa --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/utils/AbstractCompactionEstimator.java @@ -0,0 +1,63 @@ +/* + * 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.iotdb.db.engine.compaction.cross.utils; + +import org.apache.iotdb.db.engine.storagegroup.TsFileResource; +import org.apache.iotdb.tsfile.read.TsFileSequenceReader; + +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Estimate the memory cost of one compaction task with specific source files based on its + * corresponding implementation. + */ +public abstract class AbstractCompactionEstimator { + + protected Map fileReaderCache = new HashMap<>(); + + /** + * Estimate the memory cost of compacting the unseq file and its corresponding overlapped seq + * files in cross space compaction task. + */ + public abstract long estimateCrossCompactionMemory( + List seqResources, TsFileResource unseqResource) throws IOException; + + /** Estimate the memory cost of compacting the source files in inner space compaction task. */ + public abstract long estimateInnerCompactionMemory(List resources); + + /** Construct a new or get an existing TsFileSequenceReader of a TsFile. */ + protected TsFileSequenceReader getFileReader(TsFileResource tsFileResource) throws IOException { + TsFileSequenceReader reader = fileReaderCache.get(tsFileResource); + if (reader == null) { + reader = new TsFileSequenceReader(tsFileResource.getTsFilePath(), true, false); + fileReaderCache.put(tsFileResource, reader); + } + return reader; + } + + public void clear() throws IOException { + for (TsFileSequenceReader sequenceReader : fileReaderCache.values()) { + sequenceReader.close(); + } + fileReaderCache.clear(); + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/rewrite/selector/IFileQueryMemMeasurement.java b/server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/utils/IFileQueryMemMeasurement.java similarity index 94% rename from server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/rewrite/selector/IFileQueryMemMeasurement.java rename to server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/utils/IFileQueryMemMeasurement.java index 251eb1849ec3..c03be63a97aa 100644 --- a/server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/rewrite/selector/IFileQueryMemMeasurement.java +++ b/server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/utils/IFileQueryMemMeasurement.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.engine.compaction.cross.rewrite.selector; +package org.apache.iotdb.db.engine.compaction.cross.utils; import org.apache.iotdb.db.engine.storagegroup.TsFileResource; diff --git a/server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/utils/InplaceCompactionEstimator.java b/server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/utils/InplaceCompactionEstimator.java new file mode 100644 index 000000000000..5f195db339c7 --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/utils/InplaceCompactionEstimator.java @@ -0,0 +1,173 @@ +/* + * 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.iotdb.db.engine.compaction.cross.utils; + +import org.apache.iotdb.db.conf.IoTDBDescriptor; +import org.apache.iotdb.db.engine.compaction.cross.AbstractCrossSpaceEstimator; +import org.apache.iotdb.db.engine.compaction.cross.rewrite.selector.RewriteCompactionFileSelector; +import org.apache.iotdb.db.engine.storagegroup.TsFileResource; +import org.apache.iotdb.tsfile.file.metadata.ChunkMetadata; +import org.apache.iotdb.tsfile.read.TsFileSequenceReader; +import org.apache.iotdb.tsfile.read.common.Path; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class InplaceCompactionEstimator extends AbstractCrossSpaceEstimator { + private static final Logger logger = LoggerFactory.getLogger(RewriteCompactionFileSelector.class); + private static final String LOG_FILE_COST = "Memory cost of file {} is {}"; + + private boolean tightEstimate; + private long maxSeqFileCost; + + // the number of timeseries being compacted at the same time + private final int concurrentSeriesNum = + IoTDBDescriptor.getInstance().getConfig().getSubCompactionTaskNum(); + + /** Total metadata size of each file. */ + private final Map fileMetaSizeMap = new HashMap<>(); + + /** Maximum memory cost of querying a timeseries in each file. */ + private final Map maxSeriesQueryCostMap = new HashMap<>(); + + public InplaceCompactionEstimator() { + this.tightEstimate = false; + this.maxSeqFileCost = 0; + } + + @Override + public long estimateCrossCompactionMemory( + List seqResources, TsFileResource unseqResource) throws IOException { + if (tightEstimate) { + return calculateTightMemoryCost(unseqResource, seqResources); + } else { + return calculateLooseMemoryCost(unseqResource, seqResources); + } + } + + private long calculateMemoryCost( + TsFileResource unseqResource, + List seqResources, + IFileQueryMemMeasurement unseqMeasurement, + IFileQueryMemMeasurement seqMeasurement) + throws IOException { + long cost = 0; + Long fileCost = unseqMeasurement.measure(unseqResource); + cost += fileCost; + + for (TsFileResource seqFile : seqResources) { + fileCost = seqMeasurement.measure(seqFile); + if (fileCost > maxSeqFileCost) { + // only one file will be read at the same time, so only the largest one is recorded here + cost -= maxSeqFileCost; + cost += fileCost; + maxSeqFileCost = fileCost; + } + // but writing data into a new file may generate the same amount of metadata in memory + cost += calculateMetadataSize(seqFile); + } + return cost; + } + + private long calculateLooseMemoryCost( + TsFileResource unseqResource, List seqResources) throws IOException { + return calculateMemoryCost( + unseqResource, seqResources, TsFileResource::getTsFileSize, this::calculateMetadataSize); + } + + private long calculateTightMemoryCost( + TsFileResource unseqResource, List seqResources) throws IOException { + return calculateMemoryCost( + unseqResource, + seqResources, + this::calculateTightUnseqMemoryCost, + this::calculateTightSeqMemoryCost); + } + + private long calculateMetadataSize(TsFileResource seqFile) throws IOException { + Long cost = fileMetaSizeMap.get(seqFile); + if (cost == null) { + cost = getFileReader(seqFile).getFileMetadataSize(); + fileMetaSizeMap.put(seqFile, cost); + logger.debug(LOG_FILE_COST, seqFile, cost); + } + return cost; + } + + private long calculateTightFileMemoryCost( + TsFileResource seqFile, IFileQueryMemMeasurement measurement) throws IOException { + Long cost = maxSeriesQueryCostMap.get(seqFile); + if (cost == null) { + long[] chunkNums = findTotalAndLargestSeriesChunkNum(seqFile, getFileReader(seqFile)); + long totalChunkNum = chunkNums[0]; + long maxChunkNum = chunkNums[1]; + cost = measurement.measure(seqFile) * maxChunkNum / totalChunkNum; + maxSeriesQueryCostMap.put(seqFile, cost); + logger.debug(LOG_FILE_COST, seqFile, cost); + } + return cost; + } + + // this method traverses all ChunkMetadata to find out which series has the most chunks and uses + // its proportion to all series to get a maximum estimation + private long calculateTightSeqMemoryCost(TsFileResource seqFile) throws IOException { + long singleSeriesCost = calculateTightFileMemoryCost(seqFile, this::calculateMetadataSize); + long multiSeriesCost = concurrentSeriesNum * singleSeriesCost; + long maxCost = calculateMetadataSize(seqFile); + return Math.min(multiSeriesCost, maxCost); + } + + // this method traverses all ChunkMetadata to find out which series has the most chunks and uses + // its proportion among all series to get a maximum estimation + private long calculateTightUnseqMemoryCost(TsFileResource unseqFile) throws IOException { + long singleSeriesCost = calculateTightFileMemoryCost(unseqFile, TsFileResource::getTsFileSize); + long multiSeriesCost = concurrentSeriesNum * singleSeriesCost; + long maxCost = unseqFile.getTsFileSize(); + return Math.min(multiSeriesCost, maxCost); + } + + // returns totalChunkNum of a file and the max number of chunks of a series + private long[] findTotalAndLargestSeriesChunkNum( + TsFileResource tsFileResource, TsFileSequenceReader sequenceReader) throws IOException { + long totalChunkNum = 0; + long maxChunkNum = Long.MIN_VALUE; + List paths = sequenceReader.getAllPaths(); + + for (Path path : paths) { + List chunkMetadataList = sequenceReader.getChunkMetadataList(path, true); + totalChunkNum += chunkMetadataList.size(); + maxChunkNum = chunkMetadataList.size() > maxChunkNum ? chunkMetadataList.size() : maxChunkNum; + } + logger.debug( + "In file {}, total chunk num {}, series max chunk num {}", + tsFileResource, + totalChunkNum, + maxChunkNum); + return new long[] {totalChunkNum, maxChunkNum}; + } + + public void setTightEstimate(boolean tightEstimate) { + this.tightEstimate = tightEstimate; + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/utils/ReadPointCrossCompactionEstimator.java b/server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/utils/ReadPointCrossCompactionEstimator.java new file mode 100644 index 000000000000..e025dd1a3ec3 --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/engine/compaction/cross/utils/ReadPointCrossCompactionEstimator.java @@ -0,0 +1,172 @@ +/* + * 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.iotdb.db.engine.compaction.cross.utils; + +import org.apache.iotdb.db.conf.IoTDBDescriptor; +import org.apache.iotdb.db.engine.compaction.cross.AbstractCrossSpaceEstimator; +import org.apache.iotdb.db.engine.storagegroup.TsFileResource; +import org.apache.iotdb.tsfile.file.metadata.TimeseriesMetadata; +import org.apache.iotdb.tsfile.read.TsFileSequenceReader; +import org.apache.iotdb.tsfile.utils.Pair; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class ReadPointCrossCompactionEstimator extends AbstractCrossSpaceEstimator { + // the max cost of reading source seq file among all source seq files of this cross compaction + // task + private long maxCostOfReadingSeqFile; + + // left is the max chunk num in chunkgroup of unseq file, right is the total chunk num of unseq + // file. + private Pair maxUnseqChunkNumInDevice; + + // it stores all chunk info of seq files. Left is the max chunk num in chunkgroup of seq file, + // right is the total chunk num of seq file. + private final List> maxSeqChunkNumInDeviceList; + + // the number of timeseries being compacted at the same time + private final int subCompactionTaskNum = + IoTDBDescriptor.getInstance().getConfig().getSubCompactionTaskNum(); + + public ReadPointCrossCompactionEstimator() { + this.maxCostOfReadingSeqFile = 0; + this.maxSeqChunkNumInDeviceList = new ArrayList<>(); + } + + @Override + public long estimateCrossCompactionMemory( + List seqResources, TsFileResource unseqResource) throws IOException { + long cost = 0; + cost += calculateReadingUnseqFile(unseqResource); + cost += calculateReadingSeqFiles(seqResources); + cost += calculatingWritingTargetFiles(seqResources, unseqResource); + maxSeqChunkNumInDeviceList.clear(); + return cost; + } + + /** + * Calculate memory cost of reading source unseq files in the cross space compaction. Double the + * total size of the timeseries to be compacted at the same time in all unseq files. + */ + private long calculateReadingUnseqFile(TsFileResource unseqResource) throws IOException { + TsFileSequenceReader reader = getFileReader(unseqResource); + int[] fileInfo = getSeriesAndDeviceChunkNum(reader); + // it is max aligned series num of one device when tsfile contains aligned series, + // else is sub compaction task num. + int concurrentSeriesNum = fileInfo[2] == -1 ? subCompactionTaskNum : fileInfo[2]; + maxUnseqChunkNumInDevice = new Pair<>(fileInfo[3], fileInfo[0]); + // it means the max size of a timeseries in this file when reading all of its chunk into memory. + // Not only reading chunk into chunk cache, but also need to deserialize data point into merge + // reader, so we have to double the cost here. + return 2 * concurrentSeriesNum * (unseqResource.getTsFileSize() * fileInfo[1] / fileInfo[0]); + } + + /** + * Calculate memory cost of reading source seq files in the cross space compaction. Double the + * maximun size of the timeseries to be compacted at the same time in one seq file, because only + * one seq file will be queried at the same time. + */ + private long calculateReadingSeqFiles(List seqResources) throws IOException { + long cost = 0; + for (TsFileResource seqResource : seqResources) { + TsFileSequenceReader reader = getFileReader(seqResource); + int[] fileInfo = getSeriesAndDeviceChunkNum(reader); + // it is max aligned series num of one device when tsfile contains aligned series, + // else is sub compaction task num. + int concurrentSeriesNum = fileInfo[2] == -1 ? subCompactionTaskNum : fileInfo[2]; + long seqFileCost = + concurrentSeriesNum * (seqResource.getTsFileSize() * fileInfo[1] / fileInfo[0]); + if (seqFileCost > maxCostOfReadingSeqFile) { + // Only one seq file will be read at the same time. + // not only reading chunk into chunk cache, but also need to deserialize data point into + // merge reader, so we have to double the cost here. + cost -= 2 * maxCostOfReadingSeqFile; + cost += 2 * seqFileCost; + maxCostOfReadingSeqFile = seqFileCost; + } + maxSeqChunkNumInDeviceList.add(new Pair<>(fileInfo[3], fileInfo[0])); + } + return cost; + } + + /** + * Calculate memory cost of writing target files in the cross space compaction. Including metadata + * size of all seq files, max chunk group size of each seq file and max chunk group size of + * corresponding overlapped unseq file. + */ + private long calculatingWritingTargetFiles( + List seqResources, TsFileResource unseqResource) throws IOException { + long cost = 0; + for (TsFileResource seqResource : seqResources) { + TsFileSequenceReader reader = getFileReader(seqResource); + // add seq file metadata size + cost += reader.getFileMetadataSize(); + // add max chunk group size of this seq tsfile + cost += + seqResource.getTsFileSize() + * maxSeqChunkNumInDeviceList.get(0).left + / maxSeqChunkNumInDeviceList.get(0).right; + } + // add max chunk group size of overlapped unseq tsfile + cost += + unseqResource.getTsFileSize() + * maxUnseqChunkNumInDevice.left + / maxUnseqChunkNumInDevice.right; + return cost; + } + + /** + * Get the details of the tsfile, the returned array contains the following elements in sequence: + * + *

total chunk num in this tsfile + * + *

max chunk num of one timeseries in this tsfile + * + *

max aligned series num in one device. If there is no aligned series in this file, then it + * turns to be -1. + * + *

max chunk num of one device in this tsfile + */ + private int[] getSeriesAndDeviceChunkNum(TsFileSequenceReader reader) throws IOException { + int totalChunkNum = 0; + int maxChunkNum = 0; + int maxAlignedSeriesNumInDevice = -1; + int maxDeviceChunkNum = 0; + Map> deviceMetadata = reader.getAllTimeseriesMetadata(true); + for (Map.Entry> entry : deviceMetadata.entrySet()) { + int deviceChunkNum = 0; + List deviceTimeseriesMetadata = entry.getValue(); + if (deviceTimeseriesMetadata.get(0).getMeasurementId().equals("")) { + // aligned device + maxAlignedSeriesNumInDevice = + Math.max(maxAlignedSeriesNumInDevice, deviceTimeseriesMetadata.size()); + } + for (TimeseriesMetadata timeseriesMetadata : deviceTimeseriesMetadata) { + deviceChunkNum += timeseriesMetadata.getChunkMetadataList().size(); + totalChunkNum += timeseriesMetadata.getChunkMetadataList().size(); + maxChunkNum = Math.max(maxChunkNum, timeseriesMetadata.getChunkMetadataList().size()); + } + maxDeviceChunkNum = Math.max(maxDeviceChunkNum, deviceChunkNum); + } + return new int[] {totalChunkNum, maxChunkNum, maxAlignedSeriesNumInDevice, maxDeviceChunkNum}; + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/engine/compaction/inner/AbstractInnerSpaceEstimator.java b/server/src/main/java/org/apache/iotdb/db/engine/compaction/inner/AbstractInnerSpaceEstimator.java new file mode 100644 index 000000000000..c5b940ebaf92 --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/engine/compaction/inner/AbstractInnerSpaceEstimator.java @@ -0,0 +1,39 @@ +/* + * 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.iotdb.db.engine.compaction.inner; + +import org.apache.iotdb.db.engine.compaction.cross.utils.AbstractCompactionEstimator; +import org.apache.iotdb.db.engine.storagegroup.TsFileResource; + +import java.io.IOException; +import java.util.List; + +/** + * Estimate the memory cost of one inner space compaction task with specific source files based on + * its corresponding implementation. + */ +public abstract class AbstractInnerSpaceEstimator extends AbstractCompactionEstimator { + public abstract long estimateInnerCompactionMemory(List resources); + + public long estimateCrossCompactionMemory( + List seqResources, TsFileResource unseqResource) throws IOException { + throw new RuntimeException( + "This kind of estimator cannot be used to estimate cross space compaction task"); + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/engine/compaction/inner/sizetiered/SizeTieredCompactionSelector.java b/server/src/main/java/org/apache/iotdb/db/engine/compaction/inner/sizetiered/SizeTieredCompactionSelector.java index dc413a13701b..bb16f60c5a40 100644 --- a/server/src/main/java/org/apache/iotdb/db/engine/compaction/inner/sizetiered/SizeTieredCompactionSelector.java +++ b/server/src/main/java/org/apache/iotdb/db/engine/compaction/inner/sizetiered/SizeTieredCompactionSelector.java @@ -24,7 +24,6 @@ import org.apache.iotdb.db.engine.compaction.CompactionTaskManager; import org.apache.iotdb.db.engine.compaction.inner.IInnerSeqSpaceSelector; import org.apache.iotdb.db.engine.compaction.inner.IInnerUnseqSpaceSelector; -import org.apache.iotdb.db.engine.storagegroup.TsFileManager; import org.apache.iotdb.db.engine.storagegroup.TsFileNameGenerator; import org.apache.iotdb.db.engine.storagegroup.TsFileResource; import org.apache.iotdb.db.engine.storagegroup.TsFileResourceStatus; @@ -60,19 +59,13 @@ public class SizeTieredCompactionSelector protected String dataRegionId; protected long timePartition; protected List tsFileResources; - protected TsFileManager tsFileManager; protected boolean sequence; public SizeTieredCompactionSelector( - String logicalStorageGroupName, - String dataRegionId, - long timePartition, - TsFileManager tsFileManager, - boolean sequence) { + String logicalStorageGroupName, String dataRegionId, long timePartition, boolean sequence) { this.logicalStorageGroupName = logicalStorageGroupName; this.dataRegionId = dataRegionId; this.timePartition = timePartition; - this.tsFileManager = tsFileManager; this.sequence = sequence; } diff --git a/server/src/main/java/org/apache/iotdb/db/engine/compaction/task/ICompactionSelector.java b/server/src/main/java/org/apache/iotdb/db/engine/compaction/task/ICompactionSelector.java index 34acc5fa732c..72063d557836 100644 --- a/server/src/main/java/org/apache/iotdb/db/engine/compaction/task/ICompactionSelector.java +++ b/server/src/main/java/org/apache/iotdb/db/engine/compaction/task/ICompactionSelector.java @@ -18,6 +18,9 @@ */ package org.apache.iotdb.db.engine.compaction.task; +import org.apache.iotdb.db.engine.compaction.constant.CrossCompactionPerformer; +import org.apache.iotdb.db.engine.compaction.cross.utils.AbstractCompactionEstimator; +import org.apache.iotdb.db.engine.compaction.cross.utils.ReadPointCrossCompactionEstimator; import org.apache.iotdb.db.engine.storagegroup.TsFileResource; import org.apache.iotdb.tsfile.utils.Pair; @@ -51,4 +54,21 @@ default List, List>> selectCrossSpaceT throw new RuntimeException("This kind of selector cannot be used to select cross space task"); } } + + static AbstractCompactionEstimator getCompactionEstimator( + CrossCompactionPerformer compactionPerformer, boolean isInnerSpace) { + switch (compactionPerformer) { + case READ_POINT: + if (!isInnerSpace) { + return new ReadPointCrossCompactionEstimator(); + } + default: + throw new RuntimeException( + "Corresponding memory estimator for " + + compactionPerformer + + " performer of " + + (isInnerSpace ? "inner" : "cross") + + " space compaction is not existed."); + } + } } diff --git a/server/src/main/java/org/apache/iotdb/db/qp/executor/PlanExecutor.java b/server/src/main/java/org/apache/iotdb/db/qp/executor/PlanExecutor.java index b29785d36984..9df6492e14ff 100644 --- a/server/src/main/java/org/apache/iotdb/db/qp/executor/PlanExecutor.java +++ b/server/src/main/java/org/apache/iotdb/db/qp/executor/PlanExecutor.java @@ -1482,7 +1482,7 @@ private void loadFile(File file, OperateFilePlan plan) throws QueryProcessExcept private void loadNewTsFileVerifyMetadata(TsFileSequenceReader tsFileSequenceReader) throws MetadataException, QueryProcessException, IOException { Map> metadataSet = - tsFileSequenceReader.getAllTimeseriesMetadata(); + tsFileSequenceReader.getAllTimeseriesMetadata(false); for (Map.Entry> entry : metadataSet.entrySet()) { String deviceId = entry.getKey(); PartialPath devicePath = new PartialPath(deviceId); diff --git a/server/src/main/java/org/apache/iotdb/db/tools/TsFileSketchTool.java b/server/src/main/java/org/apache/iotdb/db/tools/TsFileSketchTool.java index 0146db0ef3e6..d6f209ab0d69 100644 --- a/server/src/main/java/org/apache/iotdb/db/tools/TsFileSketchTool.java +++ b/server/src/main/java/org/apache/iotdb/db/tools/TsFileSketchTool.java @@ -186,13 +186,13 @@ private void printTsFileMetadata(TsFileMetadata tsFileMetaData) { printlnBoth( pw, - String.format("%20s", (reader.getFileMetadataPos() + reader.getFileMetadataSize())) + String.format("%20s", (reader.getFileMetadataPos() + reader.getTsFileMetadataSize())) + "|\t[TsFileMetadataSize] " - + reader.getFileMetadataSize()); + + reader.getTsFileMetadataSize()); printlnBoth( pw, - String.format("%20s", reader.getFileMetadataPos() + reader.getFileMetadataSize() + 4) + String.format("%20s", reader.getFileMetadataPos() + reader.getTsFileMetadataSize() + 4) + "|\t[magic tail] " + reader.readTailMagic()); } catch (IOException e) { diff --git a/server/src/main/java/org/apache/iotdb/db/utils/FileLoaderUtils.java b/server/src/main/java/org/apache/iotdb/db/utils/FileLoaderUtils.java index 96b14d0c3a3b..35d8500107b6 100644 --- a/server/src/main/java/org/apache/iotdb/db/utils/FileLoaderUtils.java +++ b/server/src/main/java/org/apache/iotdb/db/utils/FileLoaderUtils.java @@ -69,7 +69,7 @@ public static void loadOrGenerateResource(TsFileResource tsFileResource) throws public static void updateTsFileResource( TsFileSequenceReader reader, TsFileResource tsFileResource) throws IOException { for (Entry> entry : - reader.getAllTimeseriesMetadata().entrySet()) { + reader.getAllTimeseriesMetadata(false).entrySet()) { for (TimeseriesMetadata timeseriesMetaData : entry.getValue()) { tsFileResource.updateStartTime( entry.getKey(), timeseriesMetaData.getStatistics().getStartTime()); diff --git a/server/src/main/java/org/apache/iotdb/db/utils/MergeUtils.java b/server/src/main/java/org/apache/iotdb/db/utils/MergeUtils.java deleted file mode 100644 index a1d8113a523b..000000000000 --- a/server/src/main/java/org/apache/iotdb/db/utils/MergeUtils.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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.iotdb.db.utils; - -import org.apache.iotdb.db.engine.storagegroup.TsFileResource; -import org.apache.iotdb.tsfile.file.metadata.ChunkMetadata; -import org.apache.iotdb.tsfile.read.TsFileSequenceReader; -import org.apache.iotdb.tsfile.read.common.Path; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.util.List; - -public class MergeUtils { - - private static final Logger logger = LoggerFactory.getLogger(MergeUtils.class); - - private MergeUtils() { - // util class - } - - private static List collectFileSeries(TsFileSequenceReader sequenceReader) - throws IOException { - return sequenceReader.getAllPaths(); - } - - // returns totalChunkNum of a file and the max number of chunks of a series - public static long[] findTotalAndLargestSeriesChunkNum( - TsFileResource tsFileResource, TsFileSequenceReader sequenceReader) throws IOException { - long totalChunkNum = 0; - long maxChunkNum = Long.MIN_VALUE; - List paths = collectFileSeries(sequenceReader); - - for (Path path : paths) { - List chunkMetadataList = sequenceReader.getChunkMetadataList(path, true); - totalChunkNum += chunkMetadataList.size(); - maxChunkNum = chunkMetadataList.size() > maxChunkNum ? chunkMetadataList.size() : maxChunkNum; - } - logger.debug( - "In file {}, total chunk num {}, series max chunk num {}", - tsFileResource, - totalChunkNum, - maxChunkNum); - return new long[] {totalChunkNum, maxChunkNum}; - } - - public static long getFileMetaSize(TsFileResource seqFile, TsFileSequenceReader sequenceReader) { - return seqFile.getTsFileSize() - sequenceReader.getFileMetadataPos(); - } -} diff --git a/server/src/test/java/org/apache/iotdb/db/engine/compaction/cross/CrossSpaceCompactionTest.java b/server/src/test/java/org/apache/iotdb/db/engine/compaction/cross/CrossSpaceCompactionTest.java index 7ef88a52fcb4..4c3e8c595b9f 100644 --- a/server/src/test/java/org/apache/iotdb/db/engine/compaction/cross/CrossSpaceCompactionTest.java +++ b/server/src/test/java/org/apache/iotdb/db/engine/compaction/cross/CrossSpaceCompactionTest.java @@ -25,7 +25,7 @@ import org.apache.iotdb.db.engine.cache.ChunkCache; import org.apache.iotdb.db.engine.cache.TimeSeriesMetadataCache; import org.apache.iotdb.db.engine.compaction.CompactionTaskManager; -import org.apache.iotdb.db.engine.compaction.cross.rewrite.RewriteCrossSpaceCompactionResource; +import org.apache.iotdb.db.engine.compaction.cross.rewrite.CrossSpaceCompactionResource; import org.apache.iotdb.db.engine.compaction.cross.rewrite.selector.ICrossSpaceMergeFileSelector; import org.apache.iotdb.db.engine.compaction.cross.rewrite.selector.RewriteCompactionFileSelector; import org.apache.iotdb.db.engine.compaction.task.AbstractCompactionTask; @@ -420,13 +420,12 @@ public void testOneSeqFileAndSixUnseqFile() throws Exception { TsFileResourceList unseqTsFileResourceList = new TsFileResourceList(); unseqTsFileResourceList.addAll(unseqResources); long timeLowerBound = System.currentTimeMillis() - Long.MAX_VALUE; - RewriteCrossSpaceCompactionResource mergeResource = - new RewriteCrossSpaceCompactionResource( + CrossSpaceCompactionResource mergeResource = + new CrossSpaceCompactionResource( seqTsFileResourceList, unseqTsFileResourceList, timeLowerBound); ICrossSpaceMergeFileSelector fileSelector = new RewriteCompactionFileSelector(mergeResource, Long.MAX_VALUE); List[] mergeFiles = fileSelector.select(); - mergeResource.clear(); index++; if (mergeFiles.length > 0) { AbstractCompactionTask compactionTask = @@ -726,13 +725,12 @@ public void testFiveSeqFileAndOneUnseqFileWithSomeDeviceNotInSeqFiles() throws E TsFileResourceList unseqTsFileResourceList = new TsFileResourceList(); unseqTsFileResourceList.addAll(unseqResources); long timeLowerBound = System.currentTimeMillis() - Long.MAX_VALUE; - RewriteCrossSpaceCompactionResource mergeResource = - new RewriteCrossSpaceCompactionResource( + CrossSpaceCompactionResource mergeResource = + new CrossSpaceCompactionResource( seqTsFileResourceList, unseqTsFileResourceList, timeLowerBound); ICrossSpaceMergeFileSelector fileSelector = new RewriteCompactionFileSelector(mergeResource, Long.MAX_VALUE); List[] mergeFiles = fileSelector.select(); - mergeResource.clear(); if (mergeFiles.length > 0) { AbstractCompactionTask compactionTask = new CrossSpaceCompactionTask( @@ -1030,13 +1028,12 @@ public void testFiveSeqFileAndOneUnseqFile() throws Exception { TsFileResourceList unseqTsFileResourceList = new TsFileResourceList(); unseqTsFileResourceList.addAll(unseqResources); long timeLowerBound = System.currentTimeMillis() - Long.MAX_VALUE; - RewriteCrossSpaceCompactionResource mergeResource = - new RewriteCrossSpaceCompactionResource( + CrossSpaceCompactionResource mergeResource = + new CrossSpaceCompactionResource( seqTsFileResourceList, unseqTsFileResourceList, timeLowerBound); ICrossSpaceMergeFileSelector fileSelector = new RewriteCompactionFileSelector(mergeResource, Long.MAX_VALUE); List[] mergeFiles = fileSelector.select(); - mergeResource.clear(); if (mergeFiles.length > 0) { AbstractCompactionTask compactionTask = new CrossSpaceCompactionTask( diff --git a/server/src/test/java/org/apache/iotdb/db/engine/compaction/cross/MergeUpgradeTest.java b/server/src/test/java/org/apache/iotdb/db/engine/compaction/cross/MergeUpgradeTest.java index 410822ebc8eb..6e81c159b5f1 100644 --- a/server/src/test/java/org/apache/iotdb/db/engine/compaction/cross/MergeUpgradeTest.java +++ b/server/src/test/java/org/apache/iotdb/db/engine/compaction/cross/MergeUpgradeTest.java @@ -21,7 +21,7 @@ import org.apache.iotdb.commons.conf.IoTDBConstant; import org.apache.iotdb.db.constant.TestConstant; -import org.apache.iotdb.db.engine.compaction.cross.rewrite.RewriteCrossSpaceCompactionResource; +import org.apache.iotdb.db.engine.compaction.cross.rewrite.CrossSpaceCompactionResource; import org.apache.iotdb.db.engine.compaction.cross.rewrite.selector.RewriteCompactionFileSelector; import org.apache.iotdb.db.engine.compaction.utils.CompactionConfigRestorer; import org.apache.iotdb.db.engine.storagegroup.TsFileResource; @@ -79,8 +79,8 @@ public void tearDown() { @Test public void testMergeUpgradeSelect() throws MergeException { - RewriteCrossSpaceCompactionResource resource = - new RewriteCrossSpaceCompactionResource(seqResources, unseqResources); + CrossSpaceCompactionResource resource = + new CrossSpaceCompactionResource(seqResources, unseqResources); RewriteCompactionFileSelector mergeFileSelector = new RewriteCompactionFileSelector(resource, Long.MAX_VALUE); List[] result = mergeFileSelector.select(); diff --git a/server/src/test/java/org/apache/iotdb/db/engine/compaction/cross/RewriteCompactionFileSelectorTest.java b/server/src/test/java/org/apache/iotdb/db/engine/compaction/cross/RewriteCompactionFileSelectorTest.java index 121018c75211..d1f241d2a7e9 100644 --- a/server/src/test/java/org/apache/iotdb/db/engine/compaction/cross/RewriteCompactionFileSelectorTest.java +++ b/server/src/test/java/org/apache/iotdb/db/engine/compaction/cross/RewriteCompactionFileSelectorTest.java @@ -22,7 +22,7 @@ import org.apache.iotdb.commons.conf.IoTDBConstant; import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.constant.TestConstant; -import org.apache.iotdb.db.engine.compaction.cross.rewrite.RewriteCrossSpaceCompactionResource; +import org.apache.iotdb.db.engine.compaction.cross.rewrite.CrossSpaceCompactionResource; import org.apache.iotdb.db.engine.compaction.cross.rewrite.selector.ICrossSpaceMergeFileSelector; import org.apache.iotdb.db.engine.compaction.cross.rewrite.selector.RewriteCompactionFileSelector; import org.apache.iotdb.db.engine.storagegroup.TsFileResource; @@ -52,8 +52,8 @@ public class RewriteCompactionFileSelectorTest extends MergeTest { @Test public void testFullSelection() throws MergeException, IOException { - RewriteCrossSpaceCompactionResource resource = - new RewriteCrossSpaceCompactionResource(seqResources, unseqResources); + CrossSpaceCompactionResource resource = + new CrossSpaceCompactionResource(seqResources, unseqResources); ICrossSpaceMergeFileSelector mergeFileSelector = new RewriteCompactionFileSelector(resource, Long.MAX_VALUE); List[] result = mergeFileSelector.select(); @@ -61,49 +61,44 @@ public void testFullSelection() throws MergeException, IOException { List unseqSelected = result[1]; assertEquals(seqResources, seqSelected); assertEquals(unseqResources, unseqSelected); - resource.clear(); - resource = new RewriteCrossSpaceCompactionResource(seqResources.subList(0, 1), unseqResources); + resource = new CrossSpaceCompactionResource(seqResources.subList(0, 1), unseqResources); mergeFileSelector = new RewriteCompactionFileSelector(resource, Long.MAX_VALUE); result = mergeFileSelector.select(); seqSelected = result[0]; unseqSelected = result[1]; assertEquals(seqResources.subList(0, 1), seqSelected); assertEquals(unseqResources, unseqSelected); - resource.clear(); - resource = new RewriteCrossSpaceCompactionResource(seqResources, unseqResources.subList(0, 1)); + resource = new CrossSpaceCompactionResource(seqResources, unseqResources.subList(0, 1)); mergeFileSelector = new RewriteCompactionFileSelector(resource, Long.MAX_VALUE); result = mergeFileSelector.select(); seqSelected = result[0]; unseqSelected = result[1]; assertEquals(seqResources.subList(0, 1), seqSelected); assertEquals(unseqResources.subList(0, 1), unseqSelected); - resource.clear(); } @Test public void testWithFewMemoryBudgeSelection() throws MergeException, IOException { - RewriteCrossSpaceCompactionResource resource = - new RewriteCrossSpaceCompactionResource(seqResources, unseqResources); + CrossSpaceCompactionResource resource = + new CrossSpaceCompactionResource(seqResources, unseqResources); ICrossSpaceMergeFileSelector mergeFileSelector = new RewriteCompactionFileSelector(resource, 1); List[] result = mergeFileSelector.select(); assertEquals(2, result.length); - resource.clear(); } @Test public void testRestrictedSelection() throws MergeException, IOException { - RewriteCrossSpaceCompactionResource resource = - new RewriteCrossSpaceCompactionResource(seqResources, unseqResources); + CrossSpaceCompactionResource resource = + new CrossSpaceCompactionResource(seqResources, unseqResources); ICrossSpaceMergeFileSelector mergeFileSelector = new RewriteCompactionFileSelector(resource, 400000); List[] result = mergeFileSelector.select(); List seqSelected = result[0]; List unseqSelected = result[1]; - assertEquals(seqResources.subList(0, 4), seqSelected); - assertEquals(unseqResources.subList(0, 4), unseqSelected); - resource.clear(); + assertEquals(seqResources.subList(0, 5), seqSelected); + assertEquals(unseqResources.subList(0, 6), unseqSelected); } /** @@ -152,13 +147,12 @@ public void testFileOpenSelection() List newUnseqResources = new ArrayList<>(); newUnseqResources.add(largeUnseqTsFileResource); - RewriteCrossSpaceCompactionResource resource = - new RewriteCrossSpaceCompactionResource(seqResources, newUnseqResources); + CrossSpaceCompactionResource resource = + new CrossSpaceCompactionResource(seqResources, newUnseqResources); ICrossSpaceMergeFileSelector mergeFileSelector = new RewriteCompactionFileSelector(resource, Long.MAX_VALUE); List[] result = mergeFileSelector.select(); assertEquals(0, result.length); - resource.clear(); } /** @@ -207,11 +201,10 @@ public void testFileOpenSelectionFromCompaction() newUnseqResources.add(largeUnseqTsFileResource); long ttlLowerBound = System.currentTimeMillis() - Long.MAX_VALUE; - RewriteCrossSpaceCompactionResource mergeResource = - new RewriteCrossSpaceCompactionResource(seqResources, newUnseqResources, ttlLowerBound); + CrossSpaceCompactionResource mergeResource = + new CrossSpaceCompactionResource(seqResources, newUnseqResources, ttlLowerBound); assertEquals(5, mergeResource.getSeqFiles().size()); assertEquals(1, mergeResource.getUnseqFiles().size()); - mergeResource.clear(); } /** @@ -244,13 +237,12 @@ public void testFileSelectionAboutLastSeqFile() unseqResources.clear(); unseqResources.add(largeUnseqTsFileResource); - RewriteCrossSpaceCompactionResource resource = - new RewriteCrossSpaceCompactionResource(seqResources, unseqResources); + CrossSpaceCompactionResource resource = + new CrossSpaceCompactionResource(seqResources, unseqResources); ICrossSpaceMergeFileSelector mergeFileSelector = new RewriteCompactionFileSelector(resource, Long.MAX_VALUE); List[] result = mergeFileSelector.select(); assertEquals(2, result[0].size()); - resource.clear(); } @Test @@ -302,8 +294,7 @@ public void testSelectContinuousUnseqFile() prepareFile(unseqList.get(1), 0, 100, 20); prepareFile(unseqList.get(2), 99, 1, 30); - RewriteCrossSpaceCompactionResource resource = - new RewriteCrossSpaceCompactionResource(seqList, unseqList); + CrossSpaceCompactionResource resource = new CrossSpaceCompactionResource(seqList, unseqList); // the budget is enough to select unseq0 and unseq2, but not unseq1 // the first selection should only contain seq0 and unseq0 ICrossSpaceMergeFileSelector mergeFileSelector = @@ -313,16 +304,15 @@ public void testSelectContinuousUnseqFile() assertEquals(1, result[1].size()); assertEquals(seqList.get(0), result[0].get(0)); assertEquals(unseqList.get(0), result[1].get(0)); - resource.clear(); resource = - new RewriteCrossSpaceCompactionResource( + new CrossSpaceCompactionResource( seqList.subList(1, seqList.size()), unseqList.subList(1, unseqList.size())); // Although memory is out of memoryBudget, at least one unseq file should be selected mergeFileSelector = new RewriteCompactionFileSelector(resource, 29000); result = mergeFileSelector.select(); assertEquals(2, result.length); - resource.clear(); + } finally { removeFiles(seqList, unseqList); } @@ -380,8 +370,7 @@ public void testUnseqFilesOverlappedWithOneSeqFile() unseqList.add(fileResource); } - RewriteCrossSpaceCompactionResource resource = - new RewriteCrossSpaceCompactionResource(seqList, unseqList); + CrossSpaceCompactionResource resource = new CrossSpaceCompactionResource(seqList, unseqList); Assert.assertEquals(5, resource.getSeqFiles().size()); Assert.assertEquals(10, resource.getUnseqFiles().size()); ICrossSpaceMergeFileSelector mergeFileSelector = @@ -444,8 +433,7 @@ public void testOneUnseqFileOverlappedWithOneSeqFile() unseqList.add(fileResource); } - RewriteCrossSpaceCompactionResource resource = - new RewriteCrossSpaceCompactionResource(seqList, unseqList); + CrossSpaceCompactionResource resource = new CrossSpaceCompactionResource(seqList, unseqList); Assert.assertEquals(5, resource.getSeqFiles().size()); Assert.assertEquals(1, resource.getUnseqFiles().size()); ICrossSpaceMergeFileSelector mergeFileSelector = @@ -508,8 +496,7 @@ public void testUnseqFilesOverlapped() throws IOException, WriteProcessException prepareFile(unseqList.get(0), 7, 3, 7); prepareFile(unseqList.get(1), 10, 4, 10); - RewriteCrossSpaceCompactionResource resource = - new RewriteCrossSpaceCompactionResource(seqList, unseqList); + CrossSpaceCompactionResource resource = new CrossSpaceCompactionResource(seqList, unseqList); Assert.assertEquals(5, resource.getSeqFiles().size()); Assert.assertEquals(2, resource.getUnseqFiles().size()); ICrossSpaceMergeFileSelector mergeFileSelector = @@ -575,8 +562,7 @@ public void testAllUnseqFilesOverlapped() prepareFile(unseqList.get(2), 14, 3, 14); prepareFile(unseqList.get(3), 17, 2, 17); - RewriteCrossSpaceCompactionResource resource = - new RewriteCrossSpaceCompactionResource(seqList, unseqList); + CrossSpaceCompactionResource resource = new CrossSpaceCompactionResource(seqList, unseqList); Assert.assertEquals(5, resource.getSeqFiles().size()); Assert.assertEquals(4, resource.getUnseqFiles().size()); ICrossSpaceMergeFileSelector mergeFileSelector = @@ -645,8 +631,7 @@ public void testAllUnseqFilesOverlappedWithSeqFileOpen() prepareFile(unseqList.get(2), 14, 3, 14); prepareFile(unseqList.get(3), 17, 2, 17); - RewriteCrossSpaceCompactionResource resource = - new RewriteCrossSpaceCompactionResource(seqList, unseqList); + CrossSpaceCompactionResource resource = new CrossSpaceCompactionResource(seqList, unseqList); Assert.assertEquals(5, resource.getSeqFiles().size()); Assert.assertEquals(4, resource.getUnseqFiles().size()); ICrossSpaceMergeFileSelector mergeFileSelector = @@ -869,8 +854,8 @@ public void testMultiFileOverlapWithOneFile() fileWriter.flushAllChunkGroups(); fileWriter.close(); - RewriteCrossSpaceCompactionResource compactionResource = - new RewriteCrossSpaceCompactionResource(seqList, unseqList); + CrossSpaceCompactionResource compactionResource = + new CrossSpaceCompactionResource(seqList, unseqList); RewriteCompactionFileSelector selector = new RewriteCompactionFileSelector(compactionResource, 500 * 1024 * 1024); List[] result = selector.select(); @@ -883,8 +868,8 @@ public void testMaxFileSelection() throws MergeException, IOException { int oldMaxCrossCompactionCandidateFileNum = IoTDBDescriptor.getInstance().getConfig().getMaxCrossCompactionCandidateFileNum(); IoTDBDescriptor.getInstance().getConfig().setMaxCrossCompactionCandidateFileNum(5); - RewriteCrossSpaceCompactionResource resource = - new RewriteCrossSpaceCompactionResource(seqResources, unseqResources); + CrossSpaceCompactionResource resource = + new CrossSpaceCompactionResource(seqResources, unseqResources); ICrossSpaceMergeFileSelector mergeFileSelector = new RewriteCompactionFileSelector(resource, Long.MAX_VALUE); List[] result = mergeFileSelector.select(); @@ -893,7 +878,7 @@ public void testMaxFileSelection() throws MergeException, IOException { List unseqSelected = result[1]; assertEquals(2, seqSelected.size()); assertEquals(2, unseqSelected.size()); - resource.clear(); + IoTDBDescriptor.getInstance() .getConfig() .setMaxCrossCompactionCandidateFileNum(oldMaxCrossCompactionCandidateFileNum); @@ -905,8 +890,8 @@ public void testAtLeastOneUnseqFileBeenSelected() throws IOException, MergeExcep IoTDBDescriptor.getInstance().getConfig().getMaxCrossCompactionCandidateFileNum(); IoTDBDescriptor.getInstance().getConfig().setMaxCrossCompactionCandidateFileNum(1); - RewriteCrossSpaceCompactionResource resource = - new RewriteCrossSpaceCompactionResource(seqResources, unseqResources); + CrossSpaceCompactionResource resource = + new CrossSpaceCompactionResource(seqResources, unseqResources); ICrossSpaceMergeFileSelector mergeFileSelector = new RewriteCompactionFileSelector(resource, Long.MAX_VALUE); List[] result = mergeFileSelector.select(); @@ -914,7 +899,7 @@ public void testAtLeastOneUnseqFileBeenSelected() throws IOException, MergeExcep List unseqSelected = result[1]; assertEquals(1, seqSelected.size()); assertEquals(1, unseqSelected.size()); - resource.clear(); + IoTDBDescriptor.getInstance() .getConfig() .setMaxCrossCompactionCandidateFileNum(maxCrossFilesNum); diff --git a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/TsFileSequenceReader.java b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/TsFileSequenceReader.java index 265196ad306d..3c063d40deb2 100644 --- a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/TsFileSequenceReader.java +++ b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/TsFileSequenceReader.java @@ -218,10 +218,18 @@ public long getFileMetadataPos() { return fileMetadataPos; } - public int getFileMetadataSize() { + public int getTsFileMetadataSize() { return fileMetadataSize; } + /** + * Return the whole meta data size of this tsfile, including ChunkMetadata, TimeseriesMetadata and + * etc. + */ + public long getFileMetadataSize() throws IOException { + return tsFileInput.size() - getFileMetadataPos(); + } + /** this function does not modify the position of the file reader. */ public String readTailMagic() throws IOException { long totalSize = tsFileInput.size(); @@ -877,7 +885,8 @@ private void generateMetadataIndex( } /* TimeseriesMetadata don't need deserialize chunk metadata list */ - public Map> getAllTimeseriesMetadata() throws IOException { + public Map> getAllTimeseriesMetadata(boolean needChunkMetadata) + throws IOException { if (tsFileMetaData == null) { readFileMetadata(); } @@ -897,7 +906,7 @@ public Map> getAllTimeseriesMetadata() throws I null, metadataIndexNode.getNodeType(), timeseriesMetadataMap, - false); + needChunkMetadata); } return timeseriesMetadataMap; } diff --git a/tsfile/src/main/java/org/apache/iotdb/tsfile/v2/read/TsFileSequenceReaderForV2.java b/tsfile/src/main/java/org/apache/iotdb/tsfile/v2/read/TsFileSequenceReaderForV2.java index c34159f158b5..00536ca1dc74 100644 --- a/tsfile/src/main/java/org/apache/iotdb/tsfile/v2/read/TsFileSequenceReaderForV2.java +++ b/tsfile/src/main/java/org/apache/iotdb/tsfile/v2/read/TsFileSequenceReaderForV2.java @@ -426,7 +426,8 @@ private void generateMetadataIndexV2( } @Override - public Map> getAllTimeseriesMetadata() throws IOException { + public Map> getAllTimeseriesMetadata(boolean needChunkMetadata) + throws IOException { if (tsFileMetaData == null) { readFileMetadata(); } diff --git a/tsfile/src/test/java/org/apache/iotdb/tsfile/write/MetadataIndexConstructorTest.java b/tsfile/src/test/java/org/apache/iotdb/tsfile/write/MetadataIndexConstructorTest.java index 5e518d4280ed..f7137e95beee 100644 --- a/tsfile/src/test/java/org/apache/iotdb/tsfile/write/MetadataIndexConstructorTest.java +++ b/tsfile/src/test/java/org/apache/iotdb/tsfile/write/MetadataIndexConstructorTest.java @@ -234,7 +234,7 @@ private void test(String[] devices, int[][] vectorMeasurement, String[][] single assertFalse(iterator.hasNext()); Map> allTimeseriesMetadata = - reader.getAllTimeseriesMetadata(); + reader.getAllTimeseriesMetadata(false); for (int j = 0; j < actualDevices.size(); j++) { for (int i = 0; i < actualMeasurements.get(j).size(); i++) { assertEquals( diff --git a/tsfile/src/test/java/org/apache/iotdb/tsfile/write/TsFileIOWriterTest.java b/tsfile/src/test/java/org/apache/iotdb/tsfile/write/TsFileIOWriterTest.java index 4c2321928a0f..ba955912f14e 100644 --- a/tsfile/src/test/java/org/apache/iotdb/tsfile/write/TsFileIOWriterTest.java +++ b/tsfile/src/test/java/org/apache/iotdb/tsfile/write/TsFileIOWriterTest.java @@ -147,7 +147,7 @@ public void endFileTest() throws IOException { // make sure timeseriesMetadata is only Map> deviceTimeseriesMetadataMap = - reader.getAllTimeseriesMetadata(); + reader.getAllTimeseriesMetadata(false); Set pathSet = new HashSet<>(); for (Map.Entry> entry : deviceTimeseriesMetadataMap.entrySet()) { From 81b91176a0f9e3fe0dafa3caded26f41c4308a06 Mon Sep 17 00:00:00 2001 From: "Zhang.Jinrui" Date: Thu, 19 May 2022 11:44:06 +0800 Subject: [PATCH 048/436] add internal port compare when dispatching locally (#5954) --- .../db/mpp/plan/scheduler/FragmentInstanceDispatcherImpl.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/scheduler/FragmentInstanceDispatcherImpl.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/scheduler/FragmentInstanceDispatcherImpl.java index 8922de59e39f..4e446bf0850b 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/scheduler/FragmentInstanceDispatcherImpl.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/scheduler/FragmentInstanceDispatcherImpl.java @@ -61,6 +61,7 @@ public class FragmentInstanceDispatcherImpl implements IFragInstanceDispatcher { private final ExecutorService writeOperationExecutor; private final QueryType type; private final String localhostIpAddr; + private final int localhostInternalPort; private final IClientManager internalServiceClientManager; @@ -74,6 +75,7 @@ public FragmentInstanceDispatcherImpl( this.writeOperationExecutor = writeOperationExecutor; this.internalServiceClientManager = internalServiceClientManager; this.localhostIpAddr = IoTDBDescriptor.getInstance().getConfig().getInternalIp(); + this.localhostInternalPort = IoTDBDescriptor.getInstance().getConfig().getInternalPort(); } @Override @@ -138,7 +140,7 @@ private boolean dispatchOneInstance(FragmentInstance instance) } private boolean isDispatchedToLocal(TEndPoint endPoint) { - return this.localhostIpAddr.equals(endPoint.getIp()); + return this.localhostIpAddr.equals(endPoint.getIp()) && localhostInternalPort == endPoint.port; } private boolean dispatchRemote(FragmentInstance instance, TEndPoint endPoint) From b724afb4b236d15eb55eba24e89f98e3c9879e6e Mon Sep 17 00:00:00 2001 From: Liu Xuxin <37140360+THUMarkLau@users.noreply.github.com> Date: Thu, 19 May 2022 16:49:04 +0800 Subject: [PATCH 049/436] [IOTDB-3238] Fix cannot insert data into data node (#5956) --- .../planner/plan/node/write/InsertMultiTabletsNode.java | 6 ++++++ .../db/mpp/plan/planner/plan/node/write/InsertRowNode.java | 6 ++++++ .../db/mpp/plan/planner/plan/node/write/InsertRowsNode.java | 6 ++++++ .../planner/plan/node/write/InsertRowsOfOneDeviceNode.java | 6 ++++++ .../mpp/plan/planner/plan/node/write/InsertTabletNode.java | 6 ++++++ 5 files changed, 30 insertions(+) diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertMultiTabletsNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertMultiTabletsNode.java index 8f3f0200c4a4..41c37e7963e3 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertMultiTabletsNode.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertMultiTabletsNode.java @@ -27,6 +27,7 @@ import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeType; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanVisitor; import org.apache.iotdb.db.mpp.plan.planner.plan.node.WritePlanNode; import org.apache.iotdb.tsfile.exception.NotImplementedException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; @@ -279,4 +280,9 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(super.hashCode(), parentInsertTabletNodeIndexList, insertTabletNodeList); } + + @Override + public R accept(PlanVisitor visitor, C context) { + return visitor.visitInsertMultiTablets(this, context); + } } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertRowNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertRowNode.java index 0ab68df815ad..014d08d211c2 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertRowNode.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertRowNode.java @@ -33,6 +33,7 @@ import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeType; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanVisitor; import org.apache.iotdb.db.mpp.plan.planner.plan.node.WritePlanNode; import org.apache.iotdb.db.utils.CommonUtils; import org.apache.iotdb.db.utils.TypeInferenceUtils; @@ -608,4 +609,9 @@ public int hashCode() { result = 31 * result + Arrays.hashCode(values); return result; } + + @Override + public R accept(PlanVisitor visitor, C context) { + return visitor.visitInsertRow(this, context); + } } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertRowsNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertRowsNode.java index e3c82a5e3ad5..f417ebc57805 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertRowsNode.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertRowsNode.java @@ -28,6 +28,7 @@ import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeType; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanVisitor; import org.apache.iotdb.db.mpp.plan.planner.plan.node.WritePlanNode; import org.apache.iotdb.tsfile.exception.NotImplementedException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; @@ -253,4 +254,9 @@ public List splitByPartition(Analysis analysis) { return new ArrayList<>(splitMap.values()); } + + @Override + public R accept(PlanVisitor visitor, C context) { + return visitor.visitInsertRows(this, context); + } } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertRowsOfOneDeviceNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertRowsOfOneDeviceNode.java index 2fd932c4bd1d..49a6093158fe 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertRowsOfOneDeviceNode.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertRowsOfOneDeviceNode.java @@ -29,6 +29,7 @@ import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeType; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanVisitor; import org.apache.iotdb.db.mpp.plan.planner.plan.node.WritePlanNode; import org.apache.iotdb.tsfile.exception.NotImplementedException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; @@ -289,4 +290,9 @@ public List getAlignedList() { } return Collections.singletonList(insertRowNodeList.get(0).isAligned); } + + @Override + public R accept(PlanVisitor visitor, C context) { + return visitor.visitInsertRowsOfOneDevice(this, context); + } } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertTabletNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertTabletNode.java index 160b1a94e8d6..e076209edced 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertTabletNode.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/write/InsertTabletNode.java @@ -30,6 +30,7 @@ import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeType; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanVisitor; import org.apache.iotdb.db.mpp.plan.planner.plan.node.WritePlanNode; import org.apache.iotdb.db.utils.QueryDataSetUtils; import org.apache.iotdb.db.wal.buffer.IWALByteBufferView; @@ -828,4 +829,9 @@ private boolean equals(Object[] columns) { return true; } + + @Override + public R accept(PlanVisitor visitor, C context) { + return visitor.visitInsertTablet(this, context); + } } From dd1a28c58d7718fb1372b5e5a44e8d79057ecdbe Mon Sep 17 00:00:00 2001 From: ZhangHongYin <46039728+SpriCoder@users.noreply.github.com> Date: Thu, 19 May 2022 17:01:15 +0800 Subject: [PATCH 050/436] [IOTDB-3214] Refact predefined metrics in metric framework. (#5930) --- LICENSE | 18 +- .../manage/PartitionedSnapshotLogManager.java | 4 +- .../handlers/caller/ElectionHandler.java | 4 +- .../utils/nodetool/ClusterMonitor.java | 4 +- .../Maintenance-Tools/Metric-Tool.md | 205 ++++++------ .../Maintenance-Tools/Metric-Tool.md | 206 ++++++------ .../dropwizard/DropwizardMetricManager.java | 32 -- .../assembly/resources/conf/iotdb-metric.yml | 5 +- .../iotdb/metrics/DoNothingMetricService.java | 14 +- .../apache/iotdb/metrics/MetricManager.java | 8 - .../apache/iotdb/metrics/MetricService.java | 21 +- .../iotdb/metrics/config/MetricConfig.java | 3 +- .../metrics/impl/DoNothingMetricManager.java | 4 - .../iotdb/metrics/predefined/IMetricSet.java | 31 ++ .../predefined/jvm/JvmClassLoaderMetrics.java | 52 ++++ .../predefined/jvm/JvmCompileMetrics.java | 50 +++ .../metrics/predefined/jvm/JvmGcMetrics.java | 292 ++++++++++++++++++ .../predefined/jvm/JvmMemoryMetrics.java | 105 +++++++ .../metrics/predefined/jvm/JvmMetrics.java | 49 +++ .../predefined/jvm/JvmThreadMetrics.java | 87 ++++++ .../predefined/logback/LogbackMetrics.java | 180 +++++++++++ .../apache/iotdb/metrics/utils/JvmUtils.java | 46 +++ .../iotdb/metrics/utils/PredefinedMetric.java | 5 +- .../micrometer/MicrometerMetricManager.java | 51 --- .../iotdb/db/engine/cache/ChunkCache.java | 4 +- .../engine/cache/TimeSeriesMetadataCache.java | 4 +- .../compaction/CompactionMetricsManager.java | 4 +- .../iotdb/db/engine/flush/FlushManager.java | 4 +- .../db/engine/flush/MemTableFlushTask.java | 4 +- .../db/engine/memtable/AbstractMemTable.java | 4 +- .../db/engine/storagegroup/DataRegion.java | 4 +- .../storagegroup/TsFileProcessorInfo.java | 4 +- .../metadata/rescon/TimeseriesStatistics.java | 4 +- .../pool/RawQueryReadTaskPoolManager.java | 4 +- .../db/service/metrics/MetricsService.java | 150 ++------- .../service/metrics/{ => enums}/Metric.java | 2 +- .../metrics/{ => enums}/Operation.java | 2 +- .../db/service/metrics/{ => enums}/Tag.java | 2 +- .../metrics/predefined/FileMetrics.java | 137 ++++++++ .../ProcessMetrics.java} | 51 +-- .../SystemMetrics.java} | 37 ++- .../service/thrift/ProcessorWithMetrics.java | 4 +- .../thrift/impl/DataNodeTSIServiceImpl.java | 2 +- .../db/service/thrift/impl/TSServiceImpl.java | 2 +- 44 files changed, 1364 insertions(+), 541 deletions(-) create mode 100644 metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/IMetricSet.java create mode 100644 metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmClassLoaderMetrics.java create mode 100644 metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmCompileMetrics.java create mode 100644 metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmGcMetrics.java create mode 100644 metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmMemoryMetrics.java create mode 100644 metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmMetrics.java create mode 100644 metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmThreadMetrics.java create mode 100644 metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/logback/LogbackMetrics.java create mode 100644 metrics/interface/src/main/java/org/apache/iotdb/metrics/utils/JvmUtils.java rename server/src/main/java/org/apache/iotdb/db/service/metrics/{ => enums}/Metric.java (96%) rename server/src/main/java/org/apache/iotdb/db/service/metrics/{ => enums}/Operation.java (96%) rename server/src/main/java/org/apache/iotdb/db/service/metrics/{ => enums}/Tag.java (94%) create mode 100644 server/src/main/java/org/apache/iotdb/db/service/metrics/predefined/FileMetrics.java rename server/src/main/java/org/apache/iotdb/db/service/metrics/{ProcessMetricsMonitor.java => predefined/ProcessMetrics.java} (80%) rename server/src/main/java/org/apache/iotdb/db/service/metrics/{SysRunMetricsMonitor.java => predefined/SystemMetrics.java} (82%) diff --git a/LICENSE b/LICENSE index f60a85675f81..6c45bf8f5d7c 100644 --- a/LICENSE +++ b/LICENSE @@ -257,4 +257,20 @@ The following files include code modified from Eclipse Collections project. Copyright: 2021 Goldman Sachs Project page: https://www.eclipse.org/collections -License: https://github.com/eclipse/eclipse-collections/blob/master/LICENSE-EDL-1.0.txt \ No newline at end of file +License: https://github.com/eclipse/eclipse-collections/blob/master/LICENSE-EDL-1.0.txt + +-------------------------------------------------------------------------------- + +The following files include code modified from Micrometer project. + +./metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmClassLoaderMetrics +./metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmCompileMetrics +./metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmGcMetrics +./metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmMemoryMetrics +./metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmThreadMetrics +./metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/logback/LogbackMetrics +./metrics/interface/src/main/java/org/apache/iotdb/metrics/utils/JvmUtils + +Copyright: 2017 VMware +Project page: https://github.com/micrometer-metrics/micrometer +License: https://github.com/micrometer-metrics/micrometer/blob/main/LICENSE \ No newline at end of file diff --git a/cluster/src/main/java/org/apache/iotdb/cluster/log/manage/PartitionedSnapshotLogManager.java b/cluster/src/main/java/org/apache/iotdb/cluster/log/manage/PartitionedSnapshotLogManager.java index 64ad0e17c865..88713aacc5b0 100644 --- a/cluster/src/main/java/org/apache/iotdb/cluster/log/manage/PartitionedSnapshotLogManager.java +++ b/cluster/src/main/java/org/apache/iotdb/cluster/log/manage/PartitionedSnapshotLogManager.java @@ -31,9 +31,9 @@ import org.apache.iotdb.cluster.server.member.DataGroupMember; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.service.IoTDB; -import org.apache.iotdb.db.service.metrics.Metric; import org.apache.iotdb.db.service.metrics.MetricsService; -import org.apache.iotdb.db.service.metrics.Tag; +import org.apache.iotdb.db.service.metrics.enums.Metric; +import org.apache.iotdb.db.service.metrics.enums.Tag; import org.apache.iotdb.metrics.config.MetricConfigDescriptor; import org.apache.iotdb.metrics.utils.MetricLevel; import org.apache.iotdb.tsfile.write.schema.TimeseriesSchema; diff --git a/cluster/src/main/java/org/apache/iotdb/cluster/server/handlers/caller/ElectionHandler.java b/cluster/src/main/java/org/apache/iotdb/cluster/server/handlers/caller/ElectionHandler.java index 9f16f3ff7605..6ad5a665a523 100644 --- a/cluster/src/main/java/org/apache/iotdb/cluster/server/handlers/caller/ElectionHandler.java +++ b/cluster/src/main/java/org/apache/iotdb/cluster/server/handlers/caller/ElectionHandler.java @@ -21,9 +21,9 @@ import org.apache.iotdb.cluster.rpc.thrift.Node; import org.apache.iotdb.cluster.server.member.RaftMember; -import org.apache.iotdb.db.service.metrics.Metric; import org.apache.iotdb.db.service.metrics.MetricsService; -import org.apache.iotdb.db.service.metrics.Tag; +import org.apache.iotdb.db.service.metrics.enums.Metric; +import org.apache.iotdb.db.service.metrics.enums.Tag; import org.apache.iotdb.metrics.config.MetricConfigDescriptor; import org.apache.iotdb.metrics.utils.MetricLevel; diff --git a/cluster/src/main/java/org/apache/iotdb/cluster/utils/nodetool/ClusterMonitor.java b/cluster/src/main/java/org/apache/iotdb/cluster/utils/nodetool/ClusterMonitor.java index dda37f9f2585..47d0232468ae 100644 --- a/cluster/src/main/java/org/apache/iotdb/cluster/utils/nodetool/ClusterMonitor.java +++ b/cluster/src/main/java/org/apache/iotdb/cluster/utils/nodetool/ClusterMonitor.java @@ -42,9 +42,9 @@ import org.apache.iotdb.commons.service.IService; import org.apache.iotdb.commons.service.JMXService; import org.apache.iotdb.commons.service.ServiceType; -import org.apache.iotdb.db.service.metrics.Metric; import org.apache.iotdb.db.service.metrics.MetricsService; -import org.apache.iotdb.db.service.metrics.Tag; +import org.apache.iotdb.db.service.metrics.enums.Metric; +import org.apache.iotdb.db.service.metrics.enums.Tag; import org.apache.iotdb.metrics.config.MetricConfigDescriptor; import org.apache.iotdb.metrics.utils.MetricLevel; import org.apache.iotdb.tsfile.utils.Pair; diff --git a/docs/UserGuide/Maintenance-Tools/Metric-Tool.md b/docs/UserGuide/Maintenance-Tools/Metric-Tool.md index 35b44de4b656..a3e311186d45 100644 --- a/docs/UserGuide/Maintenance-Tools/Metric-Tool.md +++ b/docs/UserGuide/Maintenance-Tools/Metric-Tool.md @@ -78,150 +78,133 @@ Next, we will choose Prometheus format data as samples to describe each kind of #### 4.3.1. API -| Metric | Tag | level | Description | Sample | -| ------------------- | --------------------- | ------ | ---------------------------------------- | -------------------------------------------- | +| Metric | Tag | level | Description | Sample | +| ------------------- | --------------------- | --------- | ---------------------------------------- | -------------------------------------------- | | entry_seconds_count | name="interface name" | important | The total request count of the interface | entry_seconds_count{name="openSession",} 1.0 | | entry_seconds_sum | name="interface name" | important | The total cost seconds of the interface | entry_seconds_sum{name="openSession",} 0.024 | | entry_seconds_max | name="interface name" | important | The max latency of the interface | entry_seconds_max{name="openSession",} 0.024 | | quantity_total | name="pointsIn" | important | The total points inserted into IoTDB | quantity_total{name="pointsIn",} 1.0 | -#### 4.3.2. File +#### 4.3.2. Task +| Metric | Tag | level | Description | Sample | +| ----------------------- | ----------------------------------------------------------------------------- | --------- | -------------------------------------------------------- | --------------------------------------------------------------------------------------- | +| queue | name="compaction_inner/compaction_cross/flush",
status="running/waiting" | important | The count of current tasks in running and waiting status | queue{name="flush",status="waiting",} 0.0
queue{name="flush",status="running",} 0.0 | +| cost_task_seconds_count | name="compaction/flush" | important | The total count of tasks occurs till now | cost_task_seconds_count{name="flush",} 1.0 | +| cost_task_seconds_max | name="compaction/flush" | important | The seconds of the longest task takes till now | cost_task_seconds_max{name="flush",} 0.363 | +| cost_task_seconds_sum | name="compaction/flush" | important | The total cost seconds of all tasks till now | cost_task_seconds_sum{name="flush",} 0.363 | +| data_written | name="compaction",
type="aligned/not-aligned/total" | important | The size of data written in compaction | data_written{name="compaction",type="total",} 10240 | +| data_read | name="compaction" | important | The size of data read in compaction | data_read={name="compaction",} 10240 | -| Metric | Tag | level | Description | Sample | -| ---------- | -------------------- | ------ | ----------------------------------------------- | --------------------------- | -| file_size | name="wal/seq/unseq" | important | The current file size of wal/seq/unseq in bytes | file_size{name="wal",} 67.0 | -| file_count | name="wal/seq/unseq" | important | The current count of wal/seq/unseq files | file_count{name="seq",} 1.0 | +#### 4.3.3. Memory Usage -#### 4.3.3. Flush - -| Metric | Tag | level | Description | Sample | -| ----------------------- | ------------------------------------------- | ------ | ----------------------------------------------------------------- | --------------------------------------------------------------------------------------- | -| queue | name="flush",
status="running/waiting" | important | The count of current flushing tasks in running and waiting status | queue{name="flush",status="waiting",} 0.0
queue{name="flush",status="running",} 0.0 | -| cost_task_seconds_count | name="flush" | important | The total count of flushing occurs till now | cost_task_seconds_count{name="flush",} 1.0 | -| cost_task_seconds_max | name="flush" | important | The seconds of the longest flushing task takes till now | cost_task_seconds_max{name="flush",} 0.363 | -| cost_task_seconds_sum | name="flush" | important | The total cost seconds of all flushing tasks till now | cost_task_seconds_sum{name="flush",} 0.363 | - -#### 4.3.4. Compaction - -| Metric | Tag | level | Description | Sample | -|-------------------------|-------------------------------------------------------------------------|-------------|---------------------------------------------------------------------|---------------------------------------------------------------| -| queue | name="compaction_inner/compaction_cross",
status="running/waiting" | important | The count of current compaction tasks in running and waiting status | queue{name="compaction_inner",status="waiting",} 0.0 | -| cost_task_seconds_count | name="compaction" | important | The total count of compaction occurs till now | cost_task_seconds_count{name="compaction",} 1.0 | -| cost_task_seconds_max | name="compaction" | important | The seconds of the longest compaction task takes till now | cost_task_seconds_max{name="compaction",} 0.363 | -| cost_task_seconds_sum | name="compaction" | important | The total cost seconds of all compaction tasks till now | cost_task_seconds_sum{name="compaction",} 0.363 | -| data_written | name="compaction",
type="aligned/not-aligned/total" | important | The size of data written in compaction | data_written{name="compaction",type="total",} 10240 | -| data_read | name="compaction" | important | The size of data read in compaction | data_read={name="compaction",} 10240 | -#### 4.3.5. CPU - -| Metric | Tag | level | 说明 | 示例 | -| ---------------- | ---------- | ----- | ------------------------------------ | -------------------------------------------- | -| process_cpu_load | name="cpu" | core | current process CPU Usage (%) | process_cpu_load{name="process",} 5.0 | -| process_cpu_time | name="cpu" | core | total Process CPU Time Occupied (ns) | process_cpu_time{name="process",} 3.265625E9 | -| sys_cpu_load | name="cpu" | core | current system CPU Usage(%) | sys_cpu_load{name="system",} 15.0 | -| sys_cpu_cores | name="cpu" | core | available CPU cores | sys_cpu_cores{name="system",} 16.0 | - -#### 4.3.6. Memory Usage - -| Metric | Tag | level | Description | Sample | -| ------ | --------------------------------------- | ------ | --------------------------------------------------------------------- | --------------------------------- | +| Metric | Tag | level | Description | Sample | +| ------ | --------------------------------------- | --------- | --------------------------------------------------------------------- | --------------------------------- | | mem | name="chunkMetaData/storageGroup/mtree" | important | Current memory size of chunkMetaData/storageGroup/mtree data in bytes | mem{name="chunkMetaData",} 2050.0 | -| process_max_mem | name="memory" | core | The maximum available memory for the JVM | process_max_mem{name="process",} 3.545759744E9 | -| process_used_mem | name="memory" | core | The current available memory for the JVM | process_used_mem{name="process",} 4.6065456E7 | -| process_total_mem | name="memory" | core | The current requested memory for the JVM | process_total_mem{name="process",} 2.39599616E8 | -| process_free_mem | name="memory" | core | The free available memory for the JVM | process_free_mem{name="process",} 1.94035584E8 | -| process_mem_ratio | name="memory" | core | Memory footprint ratio of process | process_mem_ratio{name="process",} 0.0 | -| sys_total_physical_memory_size | name="memory" | core | Maximum physical memory of system | sys_total_physical_memory_size{name="system",} 1.5950999552E10 | -| sys_free_physical_memory_size | name="memory" | core | The current available memory of system | sys_free_physical_memory_size{name="system",} 4.532396032E9 | -| sys_total_swap_space_size | name="memory" | core | The maximum swap area of system | sys_total_swap_space_size{name="system",} 2.1051273216E10 | -| sys_free_swap_space_size | name="memory" | core | The available swap area of system | sys_free_swap_space_size{name="system",} 2.931576832E9 | -| sys_committed_vm_size | name="memory" | important | the amount of virtual memory available to running processes | sys_committed_vm_size{name="system",} 5.04344576E8 | - -#### 4.3.7 Process Status - -| Metric | Tag | level | 说明 | 示例 | -| --------------------- | -------------- | ----- | ------------------------------------------------------------ | ------------------------------------------- | -| process_threads_count | name="process" | core | The current number of threads | process_threads_count{name="process",} 11.0 | -| process_status | name="process" | core | The process survivor status, 1.0 means survivorship, and 0.0 means terminated | process_status{name="process",} 1.0 | - -#### 4.3.8. 磁盘 - -| Metric | Tag | level | 说明 | 示例 | -| -------------------- | ----------- | ----- | ------------------------- | ----------------------------------------------------- | -| sys_disk_total_space | name="disk" | core | The total disk space | sys_disk_total_space{name="system",} 5.10770798592E11 | -| sys_disk_free_space | name="disk" | core | The available disk space | sys_disk_free_space{name="system",} 3.63467845632E11 | - -#### 4.3.9. Cache Hit Ratio - -| Metric | Tag | level | Description | Sample | -| --------- | --------------------------------------- | ------ | ----------------------------------------------------------------------------- | --------------------------- | + +#### 4.3.4. Cache Hit Ratio + +| Metric | Tag | level | Description | Sample | +| --------- | --------------------------------------- | --------- | ----------------------------------------------------------------------------- | --------------------------- | | cache_hit | name="chunk/timeSeriesMeta/bloomFilter" | important | Cache hit ratio of chunk/timeSeriesMeta and prevention ratio of bloom filter | cache_hit{name="chunk",} 80 | -#### 4.3.19. Business Data +#### 4.3.5. Business Data -| Metric | Tag | level | Description | Sample | -| -------- | ------------------------------------- | ------ | ------------------------------------------------------------- | -------------------------------- | +| Metric | Tag | level | Description | Sample | +| -------- | ------------------------------------- | --------- | ------------------------------------------------------------- | -------------------------------- | | quantity | name="timeSeries/storageGroup/device" | important | The current count of timeSeries/storageGroup/devices in IoTDB | quantity{name="timeSeries",} 1.0 | -#### 4.3.11. Cluster +#### 4.3.6. Cluster -| Metric | Tag | level | Description | Sample | -| ------------------------- | ------------------------------- | ------ | -------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | +| Metric | Tag | level | Description | Sample | +| ------------------------- | ------------------------------- | --------- | -------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | | cluster_node_leader_count | name="{{ip}}" | important | The count of ```dataGroupLeader``` on each node, which reflects the distribution of leaders | cluster_node_leader_count{name="127.0.0.1",} 2.0 | | cluster_uncommitted_log | name="{{ip_datagroupHeader}}" | important | The count of ```uncommitted_log``` on each node in data groups it belongs to | cluster_uncommitted_log{name="127.0.0.1_Data-127.0.0.1-40010-raftId-0",} 0.0 | | cluster_node_status | name="{{ip}}" | important | The current node status, 1=online 2=offline | cluster_node_status{name="127.0.0.1",} 1.0 | | cluster_elect_total | name="{{ip}}",status="fail/win" | important | The count and result (won or failed) of elections the node participated in. | cluster_elect_total{name="127.0.0.1",status="win",} 1.0 | ### 4.4. IoTDB PreDefined Metrics Set -Users can modify the value of `predefinedMetrics` in the `iotdb-metric.yml` file to enable the predefined set of metrics, which `LOGBACK` does not support in `dropwizard`. +Users can modify the value of `predefinedMetrics` in the `iotdb-metric.yml` file to enable the predefined set of metrics,now support `JVM`, `LOGBACK`, `FILE`, `PROCESS`, `SYSYTEM`. #### 4.4.1. JVM ##### 4.4.1.1. Threads -| Metric | Tag | Description | Sample | -| -------------------------- | ------------------------------------------------------------- | ------------------------------------ | -------------------------------------------------- | -| jvm_threads_live_threads | None | The current count of threads | jvm_threads_live_threads 25.0 | -| jvm_threads_daemon_threads | None | The current count of daemon threads | jvm_threads_daemon_threads 12.0 | -| jvm_threads_peak_threads | None | The max count of threads till now | jvm_threads_peak_threads 28.0 | -| jvm_threads_states_threads | state="runnable/blocked/waiting/timed-waiting/new/terminated" | The count of threads in each status | jvm_threads_states_threads{state="runnable",} 10.0 | +| Metric | Tag | level | Description | Sample | +| -------------------------- | ------------------------------------------------------------- | --------- | ------------------------------------ | -------------------------------------------------- | +| jvm_threads_live_threads | None | Important | The current count of threads | jvm_threads_live_threads 25.0 | +| jvm_threads_daemon_threads | None | Important | The current count of daemon threads | jvm_threads_daemon_threads 12.0 | +| jvm_threads_peak_threads | None | Important | The max count of threads till now | jvm_threads_peak_threads 28.0 | +| jvm_threads_states_threads | state="runnable/blocked/waiting/timed-waiting/new/terminated" | Important | The count of threads in each status | jvm_threads_states_threads{state="runnable",} 10.0 | ##### 4.4.1.2. GC -| Metric | Tag | Description | Sample | -| ----------------------------------- | ------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | -| jvm_gc_pause_seconds_count | action="end of major GC/end of minor GC",cause="xxxx" | The total count of YGC/FGC events and its cause | jvm_gc_pause_seconds_count{action="end of major GC",cause="Metadata GC Threshold",} 1.0 | -| jvm_gc_pause_seconds_sum | action="end of major GC/end of minor GC",cause="xxxx" | The total cost seconds of YGC/FGC and its cause | jvm_gc_pause_seconds_sum{action="end of major GC",cause="Metadata GC Threshold",} 0.03 | -| jvm_gc_pause_seconds_max | action="end of major GC",cause="Metadata GC Threshold" | The max cost seconds of YGC/FGC till now and its cause | jvm_gc_pause_seconds_max{action="end of major GC",cause="Metadata GC Threshold",} 0.0 | -| jvm_gc_overhead_percent | None | An approximation of the percent of CPU time used by GC activities over the last lookback period or since monitoring began, whichever is shorter, in the range [0..1] | jvm_gc_overhead_percent 0.0 | -| jvm_gc_memory_promoted_bytes_total | None | Count of positive increases in the size of the old generation memory pool before GC to after GC | jvm_gc_memory_promoted_bytes_total 8425512.0 | -| jvm_gc_max_data_size_bytes | None | Max size of long-lived heap memory pool | jvm_gc_max_data_size_bytes 2.863661056E9 | -| jvm_gc_live_data_size_bytes | 无 | Size of long-lived heap memory pool after reclamation | jvm_gc_live_data_size_bytes 8450088.0 | -| jvm_gc_memory_allocated_bytes_total | None | Incremented for an increase in the size of the (young) heap memory pool after one GC to before the next | jvm_gc_memory_allocated_bytes_total 4.2979144E7 | +| Metric | Tag | level | Description | Sample | +| ----------------------------------- | ------------------------------------------------------ | --------- | ------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | +| jvm_gc_pause_seconds_count | action="end of major GC/end of minor GC",cause="xxxx" | Important | The total count of YGC/FGC events and its cause | jvm_gc_pause_seconds_count{action="end of major GC",cause="Metadata GC Threshold",} 1.0 | +| jvm_gc_pause_seconds_sum | action="end of major GC/end of minor GC",cause="xxxx" | Important | The total cost seconds of YGC/FGC and its cause | jvm_gc_pause_seconds_sum{action="end of major GC",cause="Metadata GC Threshold",} 0.03 | +| jvm_gc_pause_seconds_max | action="end of major GC",cause="Metadata GC Threshold" | Important | The max cost seconds of YGC/FGC till now and its cause | jvm_gc_pause_seconds_max{action="end of major GC",cause="Metadata GC Threshold",} 0.0 | +| jvm_gc_memory_promoted_bytes_total | None | Important | Count of positive increases in the size of the old generation memory pool before GC to after GC | jvm_gc_memory_promoted_bytes_total 8425512.0 | +| jvm_gc_max_data_size_bytes | None | Important | Max size of long-lived heap memory pool | jvm_gc_max_data_size_bytes 2.863661056E9 | +| jvm_gc_live_data_size_bytes | None | Important | Size of long-lived heap memory pool after reclamation | jvm_gc_live_data_size_bytes 8450088.0 | +| jvm_gc_memory_allocated_bytes_total | None | Important | Incremented for an increase in the size of the (young) heap memory pool after one GC to before the next | jvm_gc_memory_allocated_bytes_total 4.2979144E7 | ##### 4.4.1.3. Memory -| Metric | Tag | Description | Sample | -| ------------------------------- | ------------------------------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| jvm_buffer_memory_used_bytes | id="direct/mapped" | An estimate of the memory that the Java virtual machine is using for this buffer pool | jvm_buffer_memory_used_bytes{id="direct",} 3.46728099E8 | -| jvm_buffer_total_capacity_bytes | id="direct/mapped" | An estimate of the total capacity of the buffers in this pool | jvm_buffer_total_capacity_bytes{id="mapped",} 0.0 | -| jvm_buffer_count_buffers | id="direct/mapped" | An estimate of the number of buffers in the pool | jvm_buffer_count_buffers{id="direct",} 183.0 | -| jvm_memory_committed_bytes | {area="heap/nonheap",id="xxx",} | The amount of memory in bytes that is committed for the Java virtual machine to use | jvm_memory_committed_bytes{area="heap",id="Par Survivor Space",} 2.44252672E8
jvm_memory_committed_bytes{area="nonheap",id="Metaspace",} 3.9051264E7
| -| jvm_memory_max_bytes | {area="heap/nonheap",id="xxx",} | The maximum amount of memory in bytes that can be used for memory management | jvm_memory_max_bytes{area="heap",id="Par Survivor Space",} 2.44252672E8
jvm_memory_max_bytes{area="nonheap",id="Compressed Class Space",} 1.073741824E9 | -| jvm_memory_used_bytes | {area="heap/nonheap",id="xxx",} | The amount of used memory | jvm_memory_used_bytes{area="heap",id="Par Eden Space",} 1.000128376E9
jvm_memory_used_bytes{area="nonheap",id="Code Cache",} 2.9783808E7
| +| Metric | Tag | level | Description | Sample | +| ------------------------------- | ------------------------------- | --------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| jvm_buffer_memory_used_bytes | id="direct/mapped" | Important | An estimate of the memory that the Java virtual machine is using for this buffer pool | jvm_buffer_memory_used_bytes{id="direct",} 3.46728099E8 | +| jvm_buffer_total_capacity_bytes | id="direct/mapped" | Important | An estimate of the total capacity of the buffers in this pool | jvm_buffer_total_capacity_bytes{id="mapped",} 0.0 | +| jvm_buffer_count_buffers | id="direct/mapped" | Important | An estimate of the number of buffers in the pool | jvm_buffer_count_buffers{id="direct",} 183.0 | +| jvm_memory_committed_bytes | {area="heap/nonheap",id="xxx",} | Important | The amount of memory in bytes that is committed for the Java virtual machine to use | jvm_memory_committed_bytes{area="heap",id="Par Survivor Space",} 2.44252672E8
jvm_memory_committed_bytes{area="nonheap",id="Metaspace",} 3.9051264E7
| +| jvm_memory_max_bytes | {area="heap/nonheap",id="xxx",} | Important | The maximum amount of memory in bytes that can be used for memory management | jvm_memory_max_bytes{area="heap",id="Par Survivor Space",} 2.44252672E8
jvm_memory_max_bytes{area="nonheap",id="Compressed Class Space",} 1.073741824E9 | +| jvm_memory_used_bytes | {area="heap/nonheap",id="xxx",} | Important | The amount of used memory | jvm_memory_used_bytes{area="heap",id="Par Eden Space",} 1.000128376E9
jvm_memory_used_bytes{area="nonheap",id="Code Cache",} 2.9783808E7
| ##### 4.4.1.4. Classes -| Metric | Tag | Description | Sample | -| ---------------------------------- | --------------------------------------------- | ----------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | -| jvm_classes_unloaded_classes_total | 无 | The total number of classes unloaded since the Java virtual machine has started execution | jvm_classes_unloaded_classes_total 680.0 | -| jvm_classes_loaded_classes | 无 | The number of classes that are currently loaded in the Java virtual machine | jvm_classes_loaded_classes 5975.0 | -| jvm_compilation_time_ms_total | {compiler="HotSpot 64-Bit Tiered Compilers",} | The approximate accumulated elapsed time spent in compilation | jvm_compilation_time_ms_total{compiler="HotSpot 64-Bit Tiered Compilers",} 107092.0 | +| Metric | Tag | level | Description | Sample | +| ---------------------------------- | --------------------------------------------- | --------- | ----------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | +| jvm_classes_unloaded_classes_total | None | Important | The total number of classes unloaded since the Java virtual machine has started execution | jvm_classes_unloaded_classes_total 680.0 | +| jvm_classes_loaded_classes | None | Important | The number of classes that are currently loaded in the Java virtual machine | jvm_classes_loaded_classes 5975.0 | +| jvm_compilation_time_ms_total | {compiler="HotSpot 64-Bit Tiered Compilers",} | Important | The approximate accumulated elapsed time spent in compilation | jvm_compilation_time_ms_total{compiler="HotSpot 64-Bit Tiered Compilers",} 107092.0 | -#### 4.4.2. Log Events +#### 4.4.2. File -| Metric | Tag | Description | Sample | -| -------------------- | -------------------------------------- | ------------------------------------------------------------- | --------------------------------------- | -| logback_events_total | {level="trace/debug/info/warn/error",} | The count of trace/debug/info/warn/error log events till now | logback_events_total{level="warn",} 0.0 | +| Metric | Tag | level | Description | Sample | +| ---------- | -------------------- | --------- | ----------------------------------------------- | --------------------------- | +| file_size | name="wal/seq/unseq" | important | The current file size of wal/seq/unseq in bytes | file_size{name="wal",} 67.0 | +| file_count | name="wal/seq/unseq" | important | The current count of wal/seq/unseq files | file_count{name="seq",} 1.0 | + +#### 4.4.3. Logback + +| Metric | Tag | level | Description | 示例 | +| -------------------- | -------------------------------------- | --------- | ------------------------------------------------------------- | --------------------------------------- | +| logback_events_total | {level="trace/debug/info/warn/error",} | Important | The count of trace/debug/info/warn/error log events till now | logback_events_total{level="warn",} 0.0 | + +#### 4.4.4. Process +| Metric | Tag | level | Description | 示例 | +| --------------------- | -------------- | ----- | ----------------------------------------------------------------------------- | ----------------------------------------------- | +| process_cpu_load | name="cpu" | core | current process CPU Usage (%) | process_cpu_load{name="process",} 5.0 | +| process_cpu_time | name="cpu" | core | total Process CPU Time Occupied (ns) | process_cpu_time{name="process",} 3.265625E9 | +| process_max_mem | name="memory" | core | The maximum available memory for the JVM | process_max_mem{name="process",} 3.545759744E9 | +| process_used_mem | name="memory" | core | The current available memory for the JVM | process_used_mem{name="process",} 4.6065456E7 | +| process_total_mem | name="memory" | core | The current requested memory for the JVM | process_total_mem{name="process",} 2.39599616E8 | +| process_free_mem | name="memory" | core | The free available memory for the JVM | process_free_mem{name="process",} 1.94035584E8 | +| process_mem_ratio | name="memory" | core | Memory footprint ratio of process | process_mem_ratio{name="process",} 0.0 | +| process_threads_count | name="process" | core | The current number of threads | process_threads_count{name="process",} 11.0 | +| process_status | name="process" | core | The process survivor status, 1.0 means survivorship, and 0.0 means terminated | process_status{name="process",} 1.0 | + +#### 4.4.5. System +| Metric | Tag | level | Description | 示例 | +| ------------------------------ | ------------- | --------- | ----------------------------------------------------------- | -------------------------------------------------------------- | +| sys_cpu_load | name="cpu" | core | current system CPU Usage(%) | sys_cpu_load{name="system",} 15.0 | +| sys_cpu_cores | name="cpu" | core | available CPU cores | sys_cpu_cores{name="system",} 16.0 | +| sys_total_physical_memory_size | name="memory" | core | Maximum physical memory of system | sys_total_physical_memory_size{name="system",} 1.5950999552E10 | +| sys_free_physical_memory_size | name="memory" | core | The current available memory of system | sys_free_physical_memory_size{name="system",} 4.532396032E9 | +| sys_total_swap_space_size | name="memory" | core | The maximum swap area of system | sys_total_swap_space_size{name="system",} 2.1051273216E10 | +| sys_free_swap_space_size | name="memory" | core | The available swap area of system | sys_free_swap_space_size{name="system",} 2.931576832E9 | +| sys_committed_vm_size | name="memory" | important | the amount of virtual memory available to running processes | sys_committed_vm_size{name="system",} 5.04344576E8 | +| sys_disk_total_space | name="disk" | core | The total disk space | sys_disk_total_space{name="system",} 5.10770798592E11 | +| sys_disk_free_space | name="disk" | core | The available disk space | sys_disk_free_space{name="system",} 3.63467845632E11 | ### 4.5. Add custom metrics - If you want to add your own metrics data in IoTDB, please see the [IoTDB Metric Framework] (https://github.com/apache/iotdb/tree/master/metrics) document. @@ -249,6 +232,9 @@ The metrics collection switch is disabled by default,you need to enable it fro # whether enable the module enableMetric: false +# Is stat performance of operation latency +enablePerformanceStat: false + # Multiple reporter, options: [JMX, PROMETHEUS, IOTDB], IOTDB is off by default metricReporterList: - JMX @@ -260,9 +246,10 @@ monitorType: MICROMETER # Level of metric level, options: [CORE, IMPORTANT, NORMAL, ALL] metricLevel: IMPORTANT -# Predefined metric, options: [JVM, LOGBACK], LOGBACK are not supported in dropwizard +# Predefined metric, options: [JVM, LOGBACK, FILE, PROCESS, SYSTEM] predefinedMetrics: - JVM + - FILE # The http server's port for prometheus exporter to get metric data. prometheusExporterPort: 9091 diff --git a/docs/zh/UserGuide/Maintenance-Tools/Metric-Tool.md b/docs/zh/UserGuide/Maintenance-Tools/Metric-Tool.md index d627867c612d..d7e53075480b 100644 --- a/docs/zh/UserGuide/Maintenance-Tools/Metric-Tool.md +++ b/docs/zh/UserGuide/Maintenance-Tools/Metric-Tool.md @@ -76,94 +76,46 @@ IoTDB对外提供JMX和Prometheus格式的监控指标,对于JMX,可以通 #### 4.3.1. 接入层 -| Metric | Tag | level | 说明 | 示例 | -| ------------------- | --------------- | ------ | ---------------- | -------------------------------------------- | +| Metric | Tag | level | 说明 | 示例 | +| ------------------- | --------------- | --------- | ---------------- | -------------------------------------------- | | entry_seconds_count | name="接口名" | important | 接口累计访问次数 | entry_seconds_count{name="openSession",} 1.0 | | entry_seconds_sum | name="接口名" | important | 接口累计耗时(s) | entry_seconds_sum{name="openSession",} 0.024 | | entry_seconds_max | name="接口名" | important | 接口最大耗时(s) | entry_seconds_max{name="openSession",} 0.024 | | quantity_total | name="pointsIn" | important | 系统累计写入点数 | quantity_total{name="pointsIn",} 1.0 | -#### 4.3.2. 文件 +#### 4.3.2. Task -| Metric | Tag | level | 说明 | 示例 | -| ---------- | -------------------- | ------ | ----------------------------------- | --------------------------- | -| file_size | name="wal/seq/unseq" | important | 当前时间wal/seq/unseq文件大小(byte) | file_size{name="wal",} 67.0 | -| file_count | name="wal/seq/unseq" | important | 当前时间wal/seq/unseq文件个数 | file_count{name="seq",} 1.0 | +| Metric | Tag | level | 说明 | 示例 | +| ----------------------- | ----------------------------------------------------------------------------- | --------- | ------------------------------- | -------------------------------------------------------------------------------------------------- | +| queue | name="compaction_inner/compaction_cross/flush",
status="running/waiting" | important | 当前时间任务数 | queue{name="flush",status="waiting",} 0.0
queue{name="compaction/flush",status="running",} 0.0 | +| cost_task_seconds_count | name="compaction/flush" | important | 任务累计发生次数 | cost_task_seconds_count{name="flush",} 1.0 | +| cost_task_seconds_max | name="compaction/flush" | important | 到目前为止任务耗时(s)最大的一次 | cost_task_seconds_max{name="flush",} 0.363 | +| cost_task_seconds_sum | name="compaction/flush" | important | 任务累计耗时(s) | cost_task_seconds_sum{name="flush",} 0.363 | +| data_written | name="compaction",
type="aligned/not-aligned/total" | important | 合并文件时写入量 | data_written{name="compaction",type="total",} 10240 | +| data_read | name="compaction" | important | 合并文件时的读取量 | data_read={name="compaction",} 10240 | + +#### 4.3.3. 内存占用 -#### 4.3.3. Flush - -| Metric | Tag | level | 说明 | 示例 | -| ----------------------- | ------------------------------------------- | ------ | -------------------------------- | --------------------------------------------------------------------------------------- | -| queue | name="flush",
status="running/waiting" | important | 当前时间flush任务数 | queue{name="flush",status="waiting",} 0.0
queue{name="flush",status="running",} 0.0 | -| cost_task_seconds_count | name="flush" | important | flush累计发生次数 | cost_task_seconds_count{name="flush",} 1.0 | -| cost_task_seconds_max | name="flush" | important | 到目前为止flush耗时(s)最大的一次 | cost_task_seconds_max{name="flush",} 0.363 | -| cost_task_seconds_sum | name="flush" | important | flush累计耗时(s) | cost_task_seconds_sum{name="flush",} 0.363 | - -#### 4.3.4. Compaction - -| Metric | Tag | level | 说明 | 示例 | -|-------------------------|-------------------------------------------------------------------------|--------------------|---------------------------|------------------------------------------------------| -| queue | name="compaction_inner/compaction_cross",
status="running/waiting" | important | 当前时间compaction任务数 | queue{name="compaction_inner",status="waiting",} 0.0 | -| cost_task_seconds_count | name="compaction" | important | compaction累计发生次数 | cost_task_seconds_count{name="compaction",} 1.0 | -| cost_task_seconds_max | name="compaction" | important | 到目前为止compaction耗时(s)最大的一次 | cost_task_seconds_max{name="compaction",} 0.363 | -| cost_task_seconds_sum | name="compaction" | important | compaction累计耗时(s) | cost_task_seconds_sum{name="compaction",} 0.363 | -| data_written | name="compaction",
type="aligned/not-aligned/total" | important | 合并文件时写入量 | data_written{name="compaction",type="total",} 10240 | -| data_read | name="compaction" | important | 合并文件时的读取量 | data_read={name="compaction",} 10240 | - -#### 4.3.5. CPU -| Metric | Tag | level | 说明 | 示例 | -| ------ | --------------------------------------- | ------ | -------------------------------------------------- | --------------------------------- | -| process_cpu_load | name="cpu" | core | process当前CPU占用率(%) | process_cpu_load{name="process",} 5.0 | -| process_cpu_time | name="cpu" | core | process累计占用CPU时间(ns) | process_cpu_time{name="process",} 3.265625E9 | -| sys_cpu_load | name="cpu" | core | system当前CPU占用率(%) | sys_cpu_load{name="system",} 15.0 | -| sys_cpu_cores | name="cpu" | core | jvm可用处理器数 | sys_cpu_cores{name="system",} 16.0 | - -#### 4.3.6. 内存占用 - -| Metric | Tag | level | 说明 | 示例 | -| ------ | --------------------------------------- | ------ | -------------------------------------------------- | --------------------------------- | +| Metric | Tag | level | 说明 | 示例 | +| ------ | --------------------------------------- | --------- | -------------------------------------------------- | --------------------------------- | | mem | name="chunkMetaData/storageGroup/mtree" | important | chunkMetaData/storageGroup/mtree占用的内存(byte) | mem{name="chunkMetaData",} 2050.0 | -| process_max_mem | name="memory" | core | JVM最大可用内存 | process_max_mem{name="process",} 3.545759744E9 | -| process_used_mem | name="memory" | core | JVM当前使用内存 | process_used_mem{name="process",} 4.6065456E7 | -| process_total_mem | name="memory" | core | JVM当前已申请内存 | process_total_mem{name="process",} 2.39599616E8 | -| process_free_mem | name="memory" | core | JVM当前剩余可用内存 | process_free_mem{name="process",} 1.94035584E8 | -| process_mem_ratio | name="memory" | core | 进程的内存占用比例 | process_mem_ratio{name="process",} 0.0 | -| sys_total_physical_memory_size | name="memory" | core | system最大物理内存 | sys_total_physical_memory_size{name="system",} 1.5950999552E10 | -| sys_free_physical_memory_size | name="memory" | core | system当前剩余可用内存 | sys_free_physical_memory_size{name="system",} 4.532396032E9 | -| sys_total_swap_space_size | name="memory" | core | system交换区最大空间 | sys_total_swap_space_size{name="system",} 2.1051273216E10 | -| sys_free_swap_space_size | name="memory" | core | system交换区剩余可用空间 | sys_free_swap_space_size{name="system",} 2.931576832E9 | -| sys_committed_vm_size | name="memory" | important | system保证可用于正在运行的进程的虚拟内存量 | sys_committed_vm_size{name="system",} 5.04344576E8 | - -#### 4.3.7. 进程状态 - -| Metric | Tag | level | 说明 | 示例 | -| --------------------- | -------------- | ----- | ---------------------------------- | ------------------------------------------- | -| process_threads_count | name="process" | core | 当前线程数 | process_threads_count{name="process",} 11.0 | -| process_status | name="process" | core | 进程存活状态,1.0为存活,0.0为终止 | process_status{name="process",} 1.0 | - -#### 4.3.8. 磁盘 - -| Metric | Tag | level | 说明 | 示例 | -| -------------------- | ----------- | ----- | ------------ | ----------------------------------------------------- | -| sys_disk_total_space | name="disk" | core | 磁盘总大小 | sys_disk_total_space{name="system",} 5.10770798592E11 | -| sys_disk_free_space | name="disk" | core | 磁盘可用大小 | sys_disk_free_space{name="system",} 3.63467845632E11 | - -#### 4.3.9. 缓存命中率 - -| Metric | Tag | level | 说明 | 示例 | -| --------- | --------------------------------------- | ------ | ------------------------------------------------ | --------------------------- | + +#### 4.3.4. 缓存命中率 + +| Metric | Tag | level | 说明 | 示例 | +| --------- | --------------------------------------- | --------- | ------------------------------------------------ | --------------------------- | | cache_hit | name="chunk/timeSeriesMeta/bloomFilter" | important | chunk/timeSeriesMeta缓存命中率,bloomFilter拦截率 | cache_hit{name="chunk",} 80 | -#### 4.3.10. 业务数据 +#### 4.3.5. 业务数据 -| Metric | Tag | level | 说明 | 示例 | -| -------- | ------------------------------------- | ------ | -------------------------------------------- | -------------------------------- | +| Metric | Tag | level | 说明 | 示例 | +| -------- | ------------------------------------- | --------- | -------------------------------------------- | -------------------------------- | | quantity | name="timeSeries/storageGroup/device" | important | 当前时间timeSeries/storageGroup/device的数量 | quantity{name="timeSeries",} 1.0 | -#### 4.3.11. 集群 +#### 4.3.6. 集群 -| Metric | Tag | level | 说明 | 示例 | -| ------------------------- | ------------------------------- | ------ | ------------------------------------------------------------- | ---------------------------------------------------------------------------- | +| Metric | Tag | level | 说明 | 示例 | +| ------------------------- | ------------------------------- | --------- | ------------------------------------------------------------- | ---------------------------------------------------------------------------- | | cluster_node_leader_count | name="{{ip}}" | important | 节点上```dataGroupLeader```的数量,用来观察leader是否分布均匀 | cluster_node_leader_count{name="127.0.0.1",} 2.0 | | cluster_uncommitted_log | name="{{ip_datagroupHeader}}" | important | 节点```uncommitted_log```的数量 | cluster_uncommitted_log{name="127.0.0.1_Data-127.0.0.1-40010-raftId-0",} 0.0 | | cluster_node_status | name="{{ip}}" | important | 节点状态,1=online 2=offline | cluster_node_status{name="127.0.0.1",} 1.0 | @@ -171,56 +123,88 @@ IoTDB对外提供JMX和Prometheus格式的监控指标,对于JMX,可以通 ### 4.4. IoTDB 预定义指标集 -用户可以在`iotdb-metric.yml`文件中,修改`predefinedMetrics`的值来启用预定义指标集,其中`LOGBACK`在`dropwizard`中不支持。 +用户可以在`iotdb-metric.yml`文件中,修改`predefinedMetrics`的值来启用预定义指标集,目前有`JVM`、`LOGBACK`、`FILE`、`PROCESS`、`SYSYTEM`这五种。 #### 4.4.1. JVM ##### 4.4.1.1. 线程 -| Metric | Tag | 说明 | 示例 | -| -------------------------- | ------------------------------------------------------------- | ------------------------ | -------------------------------------------------- | -| jvm_threads_live_threads | 无 | 当前线程数 | jvm_threads_live_threads 25.0 | -| jvm_threads_daemon_threads | 无 | 当前daemon线程数 | jvm_threads_daemon_threads 12.0 | -| jvm_threads_peak_threads | 无 | 峰值线程数 | jvm_threads_peak_threads 28.0 | -| jvm_threads_states_threads | state="runnable/blocked/waiting/timed-waiting/new/terminated" | 当前处于各种状态的线程数 | jvm_threads_states_threads{state="runnable",} 10.0 | +| Metric | Tag | level | 说明 | 示例 | +| -------------------------- | ------------------------------------------------------------- | --------- | ------------------------ | -------------------------------------------------- | +| jvm_threads_live_threads | 无 | important | 当前线程数 | jvm_threads_live_threads 25.0 | +| jvm_threads_daemon_threads | 无 | important | 当前daemon线程数 | jvm_threads_daemon_threads 12.0 | +| jvm_threads_peak_threads | 无 | important | 峰值线程数 | jvm_threads_peak_threads 28.0 | +| jvm_threads_states_threads | state="runnable/blocked/waiting/timed-waiting/new/terminated" | important | 当前处于各种状态的线程数 | jvm_threads_states_threads{state="runnable",} 10.0 | ##### 4.4.1.2. 垃圾回收 -| Metric | Tag | 说明 | 示例 | -| ----------------------------------- | ------------------------------------------------------ | -------------------------------------------- | --------------------------------------------------------------------------------------- | -| jvm_gc_pause_seconds_count | action="end of major GC/end of minor GC",cause="xxxx" | YGC/FGC发生次数及其原因 | jvm_gc_pause_seconds_count{action="end of major GC",cause="Metadata GC Threshold",} 1.0 | -| jvm_gc_pause_seconds_sum | action="end of major GC/end of minor GC",cause="xxxx" | YGC/FGC累计耗时及其原因 | jvm_gc_pause_seconds_sum{action="end of major GC",cause="Metadata GC Threshold",} 0.03 | -| jvm_gc_pause_seconds_max | action="end of major GC",cause="Metadata GC Threshold" | YGC/FGC最大耗时及其原因 | jvm_gc_pause_seconds_max{action="end of major GC",cause="Metadata GC Threshold",} 0.0 | -| jvm_gc_overhead_percent | 无 | GC消耗cpu的比例 | jvm_gc_overhead_percent 0.0 | -| jvm_gc_memory_promoted_bytes_total | 无 | 从GC之前到GC之后老年代内存池大小正增长的累计 | jvm_gc_memory_promoted_bytes_total 8425512.0 | -| jvm_gc_max_data_size_bytes | 无 | 老年代内存的历史最大值 | jvm_gc_max_data_size_bytes 2.863661056E9 | -| jvm_gc_live_data_size_bytes | 无 | GC后老年代内存的大小 | jvm_gc_live_data_size_bytes 8450088.0 | -| jvm_gc_memory_allocated_bytes_total | 无 | 在一个GC之后到下一个GC之前年轻代增加的内存 | jvm_gc_memory_allocated_bytes_total 4.2979144E7 | +| Metric | Tag | level | 说明 | 示例 | +| ----------------------------------- | ------------------------------------------------------ | --------- | -------------------------------------------- | --------------------------------------------------------------------------------------- | +| jvm_gc_pause_seconds_count | action="end of major GC/end of minor GC",cause="xxxx" | important | YGC/FGC发生次数及其原因 | jvm_gc_pause_seconds_count{action="end of major GC",cause="Metadata GC Threshold",} 1.0 | +| jvm_gc_pause_seconds_sum | action="end of major GC/end of minor GC",cause="xxxx" | important | YGC/FGC累计耗时及其原因 | jvm_gc_pause_seconds_sum{action="end of major GC",cause="Metadata GC Threshold",} 0.03 | +| jvm_gc_pause_seconds_max | action="end of major GC",cause="Metadata GC Threshold" | important | YGC/FGC最大耗时及其原因 | jvm_gc_pause_seconds_max{action="end of major GC",cause="Metadata GC Threshold",} 0.0 | +| jvm_gc_memory_promoted_bytes_total | 无 | important | 从GC之前到GC之后老年代内存池大小正增长的累计 | jvm_gc_memory_promoted_bytes_total 8425512.0 | +| jvm_gc_max_data_size_bytes | 无 | important | 老年代内存的历史最大值 | jvm_gc_max_data_size_bytes 2.863661056E9 | +| jvm_gc_live_data_size_bytes | 无 | important | GC后老年代内存的大小 | jvm_gc_live_data_size_bytes 8450088.0 | +| jvm_gc_memory_allocated_bytes_total | 无 | important | 在一个GC之后到下一个GC之前年轻代增加的内存 | jvm_gc_memory_allocated_bytes_total 4.2979144E7 | ##### 4.4.1.3. 内存 -| Metric | Tag | 说明 | 示例 | -| ------------------------------- | ------------------------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| jvm_buffer_memory_used_bytes | id="direct/mapped" | 已经使用的缓冲区大小 | jvm_buffer_memory_used_bytes{id="direct",} 3.46728099E8 | -| jvm_buffer_total_capacity_bytes | id="direct/mapped" | 最大缓冲区大小 | jvm_buffer_total_capacity_bytes{id="mapped",} 0.0 | -| jvm_buffer_count_buffers | id="direct/mapped" | 当前缓冲区数量 | jvm_buffer_count_buffers{id="direct",} 183.0 | -| jvm_memory_committed_bytes | {area="heap/nonheap",id="xxx",} | 当前向JVM申请的内存大小 | jvm_memory_committed_bytes{area="heap",id="Par Survivor Space",} 2.44252672E8
jvm_memory_committed_bytes{area="nonheap",id="Metaspace",} 3.9051264E7
| -| jvm_memory_max_bytes | {area="heap/nonheap",id="xxx",} | JVM最大内存 | jvm_memory_max_bytes{area="heap",id="Par Survivor Space",} 2.44252672E8
jvm_memory_max_bytes{area="nonheap",id="Compressed Class Space",} 1.073741824E9 | -| jvm_memory_used_bytes | {area="heap/nonheap",id="xxx",} | JVM已使用内存大小 | jvm_memory_used_bytes{area="heap",id="Par Eden Space",} 1.000128376E9
jvm_memory_used_bytes{area="nonheap",id="Code Cache",} 2.9783808E7
| +| Metric | Tag | level | 说明 | 示例 | +| ------------------------------- | ------------------------------- | --------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| jvm_buffer_memory_used_bytes | id="direct/mapped" | important | 已经使用的缓冲区大小 | jvm_buffer_memory_used_bytes{id="direct",} 3.46728099E8 | +| jvm_buffer_total_capacity_bytes | id="direct/mapped" | important | 最大缓冲区大小 | jvm_buffer_total_capacity_bytes{id="mapped",} 0.0 | +| jvm_buffer_count_buffers | id="direct/mapped" | important | 当前缓冲区数量 | jvm_buffer_count_buffers{id="direct",} 183.0 | +| jvm_memory_committed_bytes | {area="heap/nonheap",id="xxx",} | important | 当前向JVM申请的内存大小 | jvm_memory_committed_bytes{area="heap",id="Par Survivor Space",} 2.44252672E8
jvm_memory_committed_bytes{area="nonheap",id="Metaspace",} 3.9051264E7
| +| jvm_memory_max_bytes | {area="heap/nonheap",id="xxx",} | important | JVM最大内存 | jvm_memory_max_bytes{area="heap",id="Par Survivor Space",} 2.44252672E8
jvm_memory_max_bytes{area="nonheap",id="Compressed Class Space",} 1.073741824E9 | +| jvm_memory_used_bytes | {area="heap/nonheap",id="xxx",} | important | JVM已使用内存大小 | jvm_memory_used_bytes{area="heap",id="Par Eden Space",} 1.000128376E9
jvm_memory_used_bytes{area="nonheap",id="Code Cache",} 2.9783808E7
| ##### 4.4.1.4. Classes -| Metric | Tag | 说明 | 示例 | -| ---------------------------------- | --------------------------------------------- | ---------------------- | ----------------------------------------------------------------------------------- | -| jvm_classes_unloaded_classes_total | 无 | jvm累计卸载的class数量 | jvm_classes_unloaded_classes_total 680.0 | -| jvm_classes_loaded_classes | 无 | jvm累计加载的class数量 | jvm_classes_loaded_classes 5975.0 | -| jvm_compilation_time_ms_total | {compiler="HotSpot 64-Bit Tiered Compilers",} | jvm耗费在编译上的时间 | jvm_compilation_time_ms_total{compiler="HotSpot 64-Bit Tiered Compilers",} 107092.0 | +| Metric | Tag | level | 说明 | 示例 | +| ---------------------------------- | --------------------------------------------- | --------- | ---------------------- | ----------------------------------------------------------------------------------- | +| jvm_classes_unloaded_classes_total | 无 | important | jvm累计卸载的class数量 | jvm_classes_unloaded_classes_total 680.0 | +| jvm_classes_loaded_classes | 无 | important | jvm累计加载的class数量 | jvm_classes_loaded_classes 5975.0 | +| jvm_compilation_time_ms_total | {compiler="HotSpot 64-Bit Tiered Compilers",} | important | jvm耗费在编译上的时间 | jvm_compilation_time_ms_total{compiler="HotSpot 64-Bit Tiered Compilers",} 107092.0 | -#### 4.4.2. 日志(logback) +#### 4.4.2. 文件(File) -| Metric | Tag | 说明 | 示例 | -| -------------------- | -------------------------------------- | --------------------------------------- | --------------------------------------- | -| logback_events_total | {level="trace/debug/info/warn/error",} | trace/debug/info/warn/error日志累计数量 | logback_events_total{level="warn",} 0.0 | +| Metric | Tag | level | 说明 | 示例 | +| ---------- | -------------------- | --------- | ----------------------------------- | --------------------------- | +| file_size | name="wal/seq/unseq" | important | 当前时间wal/seq/unseq文件大小(byte) | file_size{name="wal",} 67.0 | +| file_count | name="wal/seq/unseq" | important | 当前时间wal/seq/unseq文件个数 | file_count{name="seq",} 1.0 | + +#### 4.4.3. 日志(logback) + +| Metric | Tag | level | 说明 | 示例 | +| -------------------- | -------------------------------------- | --------- | --------------------------------------- | --------------------------------------- | +| logback_events_total | {level="trace/debug/info/warn/error",} | important | trace/debug/info/warn/error日志累计数量 | logback_events_total{level="warn",} 0.0 | + +#### 4.4.4. 进程(Process) +| Metric | Tag | level | 说明 | 示例 | +| --------------------- | -------------- | ----- | ---------------------------------- | ----------------------------------------------- | +| process_cpu_load | name="cpu" | core | process当前CPU占用率(%) | process_cpu_load{name="process",} 5.0 | +| process_cpu_time | name="cpu" | core | process累计占用CPU时间(ns) | process_cpu_time{name="process",} 3.265625E9 | +| process_max_mem | name="memory" | core | JVM最大可用内存 | process_max_mem{name="process",} 3.545759744E9 | +| process_used_mem | name="memory" | core | JVM当前使用内存 | process_used_mem{name="process",} 4.6065456E7 | +| process_total_mem | name="memory" | core | JVM当前已申请内存 | process_total_mem{name="process",} 2.39599616E8 | +| process_free_mem | name="memory" | core | JVM当前剩余可用内存 | process_free_mem{name="process",} 1.94035584E8 | +| process_mem_ratio | name="memory" | core | 进程的内存占用比例 | process_mem_ratio{name="process",} 0.0 | +| process_threads_count | name="process" | core | 当前线程数 | process_threads_count{name="process",} 11.0 | +| process_status | name="process" | core | 进程存活状态,1.0为存活,0.0为终止 | process_status{name="process",} 1.0 | + +#### 4.4.5. 系统(System) +| Metric | Tag | level | 说明 | 示例 | +| ------------------------------ | ------------- | --------- | ------------------------------------------ | -------------------------------------------------------------- | +| sys_cpu_load | name="cpu" | core | system当前CPU占用率(%) | sys_cpu_load{name="system",} 15.0 | +| sys_cpu_cores | name="cpu" | core | jvm可用处理器数 | sys_cpu_cores{name="system",} 16.0 | +| sys_total_physical_memory_size | name="memory" | core | system最大物理内存 | sys_total_physical_memory_size{name="system",} 1.5950999552E10 | +| sys_free_physical_memory_size | name="memory" | core | system当前剩余可用内存 | sys_free_physical_memory_size{name="system",} 4.532396032E9 | +| sys_total_swap_space_size | name="memory" | core | system交换区最大空间 | sys_total_swap_space_size{name="system",} 2.1051273216E10 | +| sys_free_swap_space_size | name="memory" | core | system交换区剩余可用空间 | sys_free_swap_space_size{name="system",} 2.931576832E9 | +| sys_committed_vm_size | name="memory" | important | system保证可用于正在运行的进程的虚拟内存量 | sys_committed_vm_size{name="system",} 5.04344576E8 | +| sys_disk_total_space | name="disk" | core | 磁盘总大小 | sys_disk_total_space{name="system",} 5.10770798592E11 | +| sys_disk_free_space | name="disk" | core | 磁盘可用大小 | sys_disk_free_space{name="system",} 3.63467845632E11 | ### 4.5. 自定义添加埋点 @@ -249,6 +233,9 @@ metric采集默认是关闭的,需要先到conf/iotdb-metric.yml中打开后 # 是否启动监控模块,默认为false enableMetric: false +# 是否启用操作延迟统计 +enablePerformanceStat: false + # 数据提供方式,对外部通过jmx和prometheus协议提供metrics的数据, 可选参数:[JMX, PROMETHEUS, IOTDB],IOTDB是默认关闭的。 metricReporterList: - JMX @@ -260,9 +247,10 @@ monitorType: MICROMETER # 初始化metric的级别,可选参数: [CORE, IMPORTANT, NORMAL, ALL] metricLevel: IMPORTANT -# 预定义的指标集, 可选参数: [JVM, LOGBACK], 其中LOGBACK在dropwizard中不支持 +# 预定义的指标集, 可选参数: [JVM, LOGBACK, FILE, PROCESS, SYSTEM] predefinedMetrics: - JVM + - FILE # Prometheus Reporter 使用的端口 prometheusExporterPort: 9091 diff --git a/metrics/dropwizard-metrics/src/main/java/org/apache/iotdb/metrics/dropwizard/DropwizardMetricManager.java b/metrics/dropwizard-metrics/src/main/java/org/apache/iotdb/metrics/dropwizard/DropwizardMetricManager.java index fc2b5cda55bd..294a771346d4 100644 --- a/metrics/dropwizard-metrics/src/main/java/org/apache/iotdb/metrics/dropwizard/DropwizardMetricManager.java +++ b/metrics/dropwizard-metrics/src/main/java/org/apache/iotdb/metrics/dropwizard/DropwizardMetricManager.java @@ -36,20 +36,13 @@ import org.apache.iotdb.metrics.type.Rate; import org.apache.iotdb.metrics.type.Timer; import org.apache.iotdb.metrics.utils.MetricLevel; -import org.apache.iotdb.metrics.utils.PredefinedMetric; import com.codahale.metrics.MetricFilter; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.UniformReservoir; -import com.codahale.metrics.jvm.BufferPoolMetricSet; -import com.codahale.metrics.jvm.CachedThreadStatesGaugeSet; -import com.codahale.metrics.jvm.ClassLoadingGaugeSet; -import com.codahale.metrics.jvm.GarbageCollectorMetricSet; -import com.codahale.metrics.jvm.JvmAttributeGaugeSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.lang.management.ManagementFactory; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -415,31 +408,6 @@ public MetricRegistry getMetricRegistry() { return metricRegistry; } - @Override - public void enablePredefinedMetric(PredefinedMetric metric) { - if (!isEnable()) { - return; - } - switch (metric) { - case JVM: - enableJvmMetrics(); - break; - default: - logger.warn("Unsupported metric type {}", metric); - } - } - - private void enableJvmMetrics() { - if (!isEnable()) { - return; - } - metricRegistry.registerAll(new JvmAttributeGaugeSet()); - metricRegistry.registerAll(new GarbageCollectorMetricSet()); - metricRegistry.registerAll(new ClassLoadingGaugeSet()); - metricRegistry.registerAll(new BufferPoolMetricSet(ManagementFactory.getPlatformMBeanServer())); - metricRegistry.registerAll(new CachedThreadStatesGaugeSet(5, TimeUnit.MILLISECONDS)); - } - @Override public boolean init() { // init something diff --git a/metrics/interface/src/main/assembly/resources/conf/iotdb-metric.yml b/metrics/interface/src/main/assembly/resources/conf/iotdb-metric.yml index 39c498747a28..cf64f6ad8818 100644 --- a/metrics/interface/src/main/assembly/resources/conf/iotdb-metric.yml +++ b/metrics/interface/src/main/assembly/resources/conf/iotdb-metric.yml @@ -20,7 +20,7 @@ # whether enable the module enableMetric: false -# Is stat performance of sub-module enable +# Is stat performance of operation latency enablePerformanceStat: false # Multiple reporter, options: [JMX, PROMETHEUS, IOTDB], IOTDB is off by default @@ -34,9 +34,10 @@ monitorType: MICROMETER # Level of metric level, options: [CORE, IMPORTANT, NORMAL, ALL] metricLevel: IMPORTANT -# Predefined metric, options: [JVM, LOGBACK], LOGBACK are not supported in dropwizard +# Predefined metric, options: [JVM, LOGBACK, FILE, PROCESS, SYSTEM] predefinedMetrics: - JVM + - FILE # The http server's port for prometheus exporter to get metric data. prometheusExporterPort: 9091 diff --git a/metrics/interface/src/main/java/org/apache/iotdb/metrics/DoNothingMetricService.java b/metrics/interface/src/main/java/org/apache/iotdb/metrics/DoNothingMetricService.java index b996b8e5edd3..32cec0b1b70c 100644 --- a/metrics/interface/src/main/java/org/apache/iotdb/metrics/DoNothingMetricService.java +++ b/metrics/interface/src/main/java/org/apache/iotdb/metrics/DoNothingMetricService.java @@ -20,22 +20,12 @@ package org.apache.iotdb.metrics; import org.apache.iotdb.metrics.config.ReloadLevel; +import org.apache.iotdb.metrics.utils.PredefinedMetric; public class DoNothingMetricService extends MetricService { - @Override - protected void collectFileSystemInfo() { - // do nothing - } @Override - protected void collectProcessInfo() { - // do nothing - } - - @Override - protected void collectSystemInfo() { - // do nothing - } + public void enablePredefinedMetric(PredefinedMetric metric) {} @Override protected void reloadProperties(ReloadLevel reloadLevel) { diff --git a/metrics/interface/src/main/java/org/apache/iotdb/metrics/MetricManager.java b/metrics/interface/src/main/java/org/apache/iotdb/metrics/MetricManager.java index 680447be0327..4a293abcc41a 100644 --- a/metrics/interface/src/main/java/org/apache/iotdb/metrics/MetricManager.java +++ b/metrics/interface/src/main/java/org/apache/iotdb/metrics/MetricManager.java @@ -25,7 +25,6 @@ import org.apache.iotdb.metrics.type.Rate; import org.apache.iotdb.metrics.type.Timer; import org.apache.iotdb.metrics.utils.MetricLevel; -import org.apache.iotdb.metrics.utils.PredefinedMetric; import java.util.List; import java.util.Map; @@ -159,13 +158,6 @@ Gauge getOrCreateAutoGauge( /** whether is enabled monitor in specific level */ boolean isEnable(MetricLevel metricLevel); - /** - * enable pre-defined metric set. - * - * @param metric which metric set we want to collect - */ - void enablePredefinedMetric(PredefinedMetric metric); - /** * init something. * diff --git a/metrics/interface/src/main/java/org/apache/iotdb/metrics/MetricService.java b/metrics/interface/src/main/java/org/apache/iotdb/metrics/MetricService.java index 10867edb5930..5ea4e576413c 100644 --- a/metrics/interface/src/main/java/org/apache/iotdb/metrics/MetricService.java +++ b/metrics/interface/src/main/java/org/apache/iotdb/metrics/MetricService.java @@ -71,14 +71,6 @@ public void startService() { enablePredefinedMetric(predefinedMetric); } logger.info("Start metric at level: " + metricConfig.getMetricLevel().name()); - - collectFileSystemInfo(); - - // Collect process monitoring information - collectProcessInfo(); - - // Collect system monitoring information - collectSystemInfo(); } /** Stop metric service. if is disabled, do nothing */ @@ -156,18 +148,7 @@ public void stop(ReporterType reporter) { * Enable some predefined metric, now support jvm, logback. Notice: In dropwizard mode, logback * metrics are not supported */ - public void enablePredefinedMetric(PredefinedMetric metric) { - metricManager.enablePredefinedMetric(metric); - } - - /** collect file system info in metric way */ - protected abstract void collectFileSystemInfo(); - - /** collect process info in metric way */ - protected abstract void collectProcessInfo(); - - /** collect system hardware info in metric way */ - protected abstract void collectSystemInfo(); + public abstract void enablePredefinedMetric(PredefinedMetric metric); /** * support hot load of some properties diff --git a/metrics/interface/src/main/java/org/apache/iotdb/metrics/config/MetricConfig.java b/metrics/interface/src/main/java/org/apache/iotdb/metrics/config/MetricConfig.java index 2c8444b7f3db..3cb02fa27347 100644 --- a/metrics/interface/src/main/java/org/apache/iotdb/metrics/config/MetricConfig.java +++ b/metrics/interface/src/main/java/org/apache/iotdb/metrics/config/MetricConfig.java @@ -25,7 +25,6 @@ import org.apache.iotdb.metrics.utils.ReporterType; import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Objects; @@ -46,7 +45,7 @@ public class MetricConfig { private MetricLevel metricLevel = MetricLevel.IMPORTANT; private List predefinedMetrics = - Collections.singletonList(PredefinedMetric.JVM); + Arrays.asList(PredefinedMetric.JVM, PredefinedMetric.FILE); /** the http server's port for prometheus exporter to get metric data. */ private String prometheusExporterPort = "9091"; diff --git a/metrics/interface/src/main/java/org/apache/iotdb/metrics/impl/DoNothingMetricManager.java b/metrics/interface/src/main/java/org/apache/iotdb/metrics/impl/DoNothingMetricManager.java index ff2d160aef53..3a1d65bbbe06 100644 --- a/metrics/interface/src/main/java/org/apache/iotdb/metrics/impl/DoNothingMetricManager.java +++ b/metrics/interface/src/main/java/org/apache/iotdb/metrics/impl/DoNothingMetricManager.java @@ -26,7 +26,6 @@ import org.apache.iotdb.metrics.type.Rate; import org.apache.iotdb.metrics.type.Timer; import org.apache.iotdb.metrics.utils.MetricLevel; -import org.apache.iotdb.metrics.utils.PredefinedMetric; import java.util.Collections; import java.util.List; @@ -139,9 +138,6 @@ public boolean isEnable(MetricLevel metricLevel) { return false; } - @Override - public void enablePredefinedMetric(PredefinedMetric metric) {} - @Override public boolean init() { return false; diff --git a/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/IMetricSet.java b/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/IMetricSet.java new file mode 100644 index 000000000000..349934b7cd60 --- /dev/null +++ b/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/IMetricSet.java @@ -0,0 +1,31 @@ +/* + * 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.iotdb.metrics.predefined; + +import org.apache.iotdb.metrics.MetricManager; +import org.apache.iotdb.metrics.utils.PredefinedMetric; + +public interface IMetricSet { + /** bind related metric to metric manager */ + void bindTo(MetricManager metricManager); + + /** get type of metric set */ + PredefinedMetric getType(); +} diff --git a/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmClassLoaderMetrics.java b/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmClassLoaderMetrics.java new file mode 100644 index 000000000000..007f4ba5b782 --- /dev/null +++ b/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmClassLoaderMetrics.java @@ -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.iotdb.metrics.predefined.jvm; + +import org.apache.iotdb.metrics.MetricManager; +import org.apache.iotdb.metrics.predefined.IMetricSet; +import org.apache.iotdb.metrics.utils.MetricLevel; +import org.apache.iotdb.metrics.utils.PredefinedMetric; + +import java.lang.management.ClassLoadingMXBean; +import java.lang.management.ManagementFactory; + +/** This file is modified from io.micrometer.core.instrument.binder.jvm.ClassLoaderMetrics */ +public class JvmClassLoaderMetrics implements IMetricSet { + @Override + public void bindTo(MetricManager metricManager) { + ClassLoadingMXBean classLoadingBean = ManagementFactory.getClassLoadingMXBean(); + + metricManager.getOrCreateAutoGauge( + "jvm.classes.loaded.classes", + MetricLevel.IMPORTANT, + classLoadingBean, + ClassLoadingMXBean::getLoadedClassCount); + metricManager.getOrCreateAutoGauge( + "jvm.classes.unloaded.classes", + MetricLevel.IMPORTANT, + classLoadingBean, + ClassLoadingMXBean::getUnloadedClassCount); + } + + @Override + public PredefinedMetric getType() { + return PredefinedMetric.JVM; + } +} diff --git a/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmCompileMetrics.java b/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmCompileMetrics.java new file mode 100644 index 000000000000..5f59492964cc --- /dev/null +++ b/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmCompileMetrics.java @@ -0,0 +1,50 @@ +/* + * 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.iotdb.metrics.predefined.jvm; + +import org.apache.iotdb.metrics.MetricManager; +import org.apache.iotdb.metrics.predefined.IMetricSet; +import org.apache.iotdb.metrics.utils.MetricLevel; +import org.apache.iotdb.metrics.utils.PredefinedMetric; + +import java.lang.management.CompilationMXBean; +import java.lang.management.ManagementFactory; + +/** This file is modified from io.micrometer.core.instrument.binder.jvm.JvmCompilationMetrics */ +public class JvmCompileMetrics implements IMetricSet { + @Override + public void bindTo(MetricManager metricManager) { + CompilationMXBean compilationBean = ManagementFactory.getCompilationMXBean(); + if (compilationBean != null && compilationBean.isCompilationTimeMonitoringSupported()) { + metricManager.getOrCreateAutoGauge( + "jvm.compilation.time.ms", + MetricLevel.IMPORTANT, + compilationBean, + CompilationMXBean::getTotalCompilationTime, + "compiler", + compilationBean.getName()); + } + } + + @Override + public PredefinedMetric getType() { + return PredefinedMetric.JVM; + } +} diff --git a/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmGcMetrics.java b/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmGcMetrics.java new file mode 100644 index 000000000000..43f1adf0bd8b --- /dev/null +++ b/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmGcMetrics.java @@ -0,0 +1,292 @@ +/* + * 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.iotdb.metrics.predefined.jvm; + +import org.apache.iotdb.metrics.MetricManager; +import org.apache.iotdb.metrics.predefined.IMetricSet; +import org.apache.iotdb.metrics.type.Counter; +import org.apache.iotdb.metrics.type.Timer; +import org.apache.iotdb.metrics.utils.JvmUtils; +import org.apache.iotdb.metrics.utils.MetricLevel; +import org.apache.iotdb.metrics.utils.PredefinedMetric; + +import com.sun.management.GarbageCollectionNotificationInfo; +import com.sun.management.GcInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.management.ListenerNotFoundException; +import javax.management.NotificationEmitter; +import javax.management.NotificationListener; +import javax.management.openmbean.CompositeData; + +import java.lang.management.GarbageCollectorMXBean; +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryPoolMXBean; +import java.lang.management.MemoryType; +import java.lang.management.MemoryUsage; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +/** This file is modified from io.micrometer.core.instrument.binder.jvm.JvmGcMetrics */ +public class JvmGcMetrics implements IMetricSet, AutoCloseable { + private static final Logger logger = LoggerFactory.getLogger(JvmGcMetrics.class); + private String youngGenPoolName; + private String oldGenPoolName; + private String nonGenerationalMemoryPool; + private final List notificationListenerCleanUpRunnables = new CopyOnWriteArrayList<>(); + + public JvmGcMetrics() { + for (MemoryPoolMXBean mbean : ManagementFactory.getMemoryPoolMXBeans()) { + String name = mbean.getName(); + if (isYoungGenPool(name)) { + youngGenPoolName = name; + } else if (isOldGenPool(name)) { + oldGenPoolName = name; + } else if (isNonGenerationalHeapPool(name)) { + nonGenerationalMemoryPool = name; + } + } + } + + @Override + public void bindTo(MetricManager metricManager) { + if (ManagementFactory.getMemoryPoolMXBeans().isEmpty()) { + logger.warn( + "GC notifications will not be available because MemoryPoolMXBeans are not provided by the JVM"); + return; + } + + try { + Class.forName( + "com.sun.management.GarbageCollectionNotificationInfo", + false, + MemoryPoolMXBean.class.getClassLoader()); + } catch (Throwable e) { + // We are operating in a JVM without access to this level of detail + logger.warn( + "GC notifications will not be available because " + + "com.sun.management.GarbageCollectionNotificationInfo is not present"); + return; + } + + double maxLongLivedPoolBytes = + ManagementFactory.getPlatformMXBeans(MemoryPoolMXBean.class).stream() + .filter(mem -> MemoryType.HEAP.equals(mem.getType())) + .filter(mem -> isOldGenPool(mem.getName()) || isNonGenerationalHeapPool(mem.getName())) + .findAny() + .map(mem -> JvmUtils.getUsageValue(mem, MemoryUsage::getMax)) + .orElse(0.0); + + AtomicLong maxDataSize = new AtomicLong((long) maxLongLivedPoolBytes); + metricManager.getOrCreateAutoGauge( + "jvm.gc.max.data.size.bytes", MetricLevel.IMPORTANT, maxDataSize, AtomicLong::get); + + AtomicLong liveDataSize = new AtomicLong(); + metricManager.getOrCreateAutoGauge( + "jvm.gc.live.data.size.bytes", MetricLevel.IMPORTANT, liveDataSize, AtomicLong::get); + + Counter allocatedBytes = + metricManager.getOrCreateCounter("jvm.gc.memory.allocated.bytes", MetricLevel.IMPORTANT); + + Counter promotedBytes = + (oldGenPoolName == null) + ? null + : metricManager.getOrCreateCounter( + "jvm.gc.memory.promoted.bytes", MetricLevel.IMPORTANT); + + // start watching for GC notifications + final AtomicLong heapPoolSizeAfterGc = new AtomicLong(); + + for (GarbageCollectorMXBean mbean : ManagementFactory.getGarbageCollectorMXBeans()) { + if (!(mbean instanceof NotificationEmitter)) { + continue; + } + NotificationListener notificationListener = + (notification, ref) -> { + CompositeData cd = (CompositeData) notification.getUserData(); + GarbageCollectionNotificationInfo notificationInfo = + GarbageCollectionNotificationInfo.from(cd); + + String gcCause = notificationInfo.getGcCause(); + String gcAction = notificationInfo.getGcAction(); + GcInfo gcInfo = notificationInfo.getGcInfo(); + long duration = gcInfo.getDuration(); + String timerName; + if (isConcurrentPhase(gcCause, notificationInfo.getGcName())) { + timerName = "jvm.gc.concurrent.phase.time"; + } else { + timerName = "jvm.gc.pause"; + } + Timer timer = + metricManager.getOrCreateTimer( + timerName, MetricLevel.IMPORTANT, "action", gcAction, "cause", gcCause); + timer.update(duration, TimeUnit.MILLISECONDS); + + // Update promotion and allocation counters + final Map before = gcInfo.getMemoryUsageBeforeGc(); + final Map after = gcInfo.getMemoryUsageAfterGc(); + + if (nonGenerationalMemoryPool != null) { + countPoolSizeDelta( + gcInfo.getMemoryUsageBeforeGc(), + gcInfo.getMemoryUsageAfterGc(), + allocatedBytes, + heapPoolSizeAfterGc, + nonGenerationalMemoryPool); + if (after.get(nonGenerationalMemoryPool).getUsed() + < before.get(nonGenerationalMemoryPool).getUsed()) { + liveDataSize.set(after.get(nonGenerationalMemoryPool).getUsed()); + final long longLivedMaxAfter = after.get(nonGenerationalMemoryPool).getMax(); + maxDataSize.set(longLivedMaxAfter); + } + return; + } + + if (oldGenPoolName != null) { + final long oldBefore = before.get(oldGenPoolName).getUsed(); + final long oldAfter = after.get(oldGenPoolName).getUsed(); + final long delta = oldAfter - oldBefore; + if (delta > 0L) { + promotedBytes.inc(delta); + } + + // Some GC implementations such as G1 can reduce the old gen size as part of a minor + // GC. To track the + // live data size we record the value if we see a reduction in the old gen heap size + // or + // after a major GC. + if (oldAfter < oldBefore + || GcGenerationAge.fromName(notificationInfo.getGcName()) + == GcGenerationAge.OLD) { + liveDataSize.set(oldAfter); + final long oldMaxAfter = after.get(oldGenPoolName).getMax(); + maxDataSize.set(oldMaxAfter); + } + } + + if (youngGenPoolName != null) { + countPoolSizeDelta( + gcInfo.getMemoryUsageBeforeGc(), + gcInfo.getMemoryUsageAfterGc(), + allocatedBytes, + heapPoolSizeAfterGc, + youngGenPoolName); + } + }; + NotificationEmitter notificationEmitter = (NotificationEmitter) mbean; + notificationEmitter.addNotificationListener( + notificationListener, + notification -> + notification + .getType() + .equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION), + null); + notificationListenerCleanUpRunnables.add( + () -> { + try { + notificationEmitter.removeNotificationListener(notificationListener); + } catch (ListenerNotFoundException ignore) { + } + }); + } + } + + private void countPoolSizeDelta( + Map before, + Map after, + Counter counter, + AtomicLong previousPoolSize, + String poolName) { + final long beforeBytes = before.get(poolName).getUsed(); + final long afterBytes = after.get(poolName).getUsed(); + final long delta = beforeBytes - previousPoolSize.get(); + previousPoolSize.set(afterBytes); + if (delta > 0L) { + counter.inc(delta); + } + } + + @Override + public void close() { + notificationListenerCleanUpRunnables.forEach(Runnable::run); + } + + @Override + public PredefinedMetric getType() { + return PredefinedMetric.JVM; + } + + enum GcGenerationAge { + OLD, + YOUNG, + UNKNOWN; + + private static Map knownCollectors = + new HashMap() { + { + put("ConcurrentMarkSweep", OLD); + put("Copy", YOUNG); + put("G1 Old Generation", OLD); + put("G1 Young Generation", YOUNG); + put("MarkSweepCompact", OLD); + put("PS MarkSweep", OLD); + put("PS Scavenge", YOUNG); + put("ParNew", YOUNG); + } + }; + + static GcGenerationAge fromName(String name) { + return knownCollectors.getOrDefault(name, UNKNOWN); + } + } + + private static Optional getLongLivedHeapPool() { + return ManagementFactory.getPlatformMXBeans(MemoryPoolMXBean.class).stream() + .filter(JvmGcMetrics::isHeap) + .filter(mem -> isOldGenPool(mem.getName()) || isNonGenerationalHeapPool(mem.getName())) + .findAny(); + } + + private static boolean isConcurrentPhase(String cause, String name) { + return "No GC".equals(cause) || "Shenandoah Cycles".equals(name); + } + + private static boolean isYoungGenPool(String name) { + return name != null && name.endsWith("Eden Space"); + } + + private static boolean isOldGenPool(String name) { + return name != null && (name.endsWith("Old Gen") || name.endsWith("Tenured Gen")); + } + + private static boolean isNonGenerationalHeapPool(String name) { + return "Shenandoah".equals(name) || "ZHeap".equals(name); + } + + private static boolean isHeap(MemoryPoolMXBean memoryPoolBean) { + return MemoryType.HEAP.equals(memoryPoolBean.getType()); + } +} diff --git a/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmMemoryMetrics.java b/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmMemoryMetrics.java new file mode 100644 index 000000000000..c8e337bd6ce4 --- /dev/null +++ b/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmMemoryMetrics.java @@ -0,0 +1,105 @@ +/* + * 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.iotdb.metrics.predefined.jvm; + +import org.apache.iotdb.metrics.MetricManager; +import org.apache.iotdb.metrics.predefined.IMetricSet; +import org.apache.iotdb.metrics.utils.JvmUtils; +import org.apache.iotdb.metrics.utils.MetricLevel; +import org.apache.iotdb.metrics.utils.PredefinedMetric; + +import java.lang.management.BufferPoolMXBean; +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryPoolMXBean; +import java.lang.management.MemoryType; +import java.lang.management.MemoryUsage; + +/** This file is modified from io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics */ +public class JvmMemoryMetrics implements IMetricSet { + @Override + public void bindTo(MetricManager metricManager) { + for (BufferPoolMXBean bufferPoolBean : + ManagementFactory.getPlatformMXBeans(BufferPoolMXBean.class)) { + metricManager.getOrCreateAutoGauge( + "jvm.buffer.count.buffers", + MetricLevel.IMPORTANT, + bufferPoolBean, + BufferPoolMXBean::getCount, + "id", + bufferPoolBean.getName()); + + metricManager.getOrCreateAutoGauge( + "jvm.buffer.memory.used.bytes", + MetricLevel.IMPORTANT, + bufferPoolBean, + BufferPoolMXBean::getMemoryUsed, + "id", + bufferPoolBean.getName()); + + metricManager.getOrCreateAutoGauge( + "jvm.buffer.total.capacity.bytes", + MetricLevel.IMPORTANT, + bufferPoolBean, + BufferPoolMXBean::getTotalCapacity, + "id", + bufferPoolBean.getName()); + } + + for (MemoryPoolMXBean memoryPoolBean : + ManagementFactory.getPlatformMXBeans(MemoryPoolMXBean.class)) { + String area = MemoryType.HEAP.equals(memoryPoolBean.getType()) ? "heap" : "nonheap"; + + metricManager.getOrCreateAutoGauge( + "jvm.memory.used.bytes", + MetricLevel.IMPORTANT, + memoryPoolBean, + (mem) -> (long) JvmUtils.getUsageValue(mem, MemoryUsage::getUsed), + "id", + memoryPoolBean.getName(), + "area", + area); + + metricManager.getOrCreateAutoGauge( + "jvm.memory.committed.bytes", + MetricLevel.IMPORTANT, + memoryPoolBean, + (mem) -> (long) JvmUtils.getUsageValue(mem, MemoryUsage::getCommitted), + "id", + memoryPoolBean.getName(), + "area", + area); + + metricManager.getOrCreateAutoGauge( + "jvm.memory.max.bytes", + MetricLevel.IMPORTANT, + memoryPoolBean, + (mem) -> (long) JvmUtils.getUsageValue(mem, MemoryUsage::getMax), + "id", + memoryPoolBean.getName(), + "area", + area); + } + } + + @Override + public PredefinedMetric getType() { + return PredefinedMetric.JVM; + } +} diff --git a/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmMetrics.java b/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmMetrics.java new file mode 100644 index 000000000000..66e81153e670 --- /dev/null +++ b/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmMetrics.java @@ -0,0 +1,49 @@ +/* + * 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.iotdb.metrics.predefined.jvm; + +import org.apache.iotdb.metrics.MetricManager; +import org.apache.iotdb.metrics.predefined.IMetricSet; +import org.apache.iotdb.metrics.utils.PredefinedMetric; + +public class JvmMetrics implements IMetricSet { + @Override + public void bindTo(MetricManager metricManager) { + JvmClassLoaderMetrics jvmClassLoaderMetricSet = new JvmClassLoaderMetrics(); + jvmClassLoaderMetricSet.bindTo(metricManager); + + JvmCompileMetrics jvmCompileMetricSet = new JvmCompileMetrics(); + jvmCompileMetricSet.bindTo(metricManager); + + JvmGcMetrics jvmGcMetricSet = new JvmGcMetrics(); + jvmGcMetricSet.bindTo(metricManager); + + JvmMemoryMetrics jvmMemoryMetricSet = new JvmMemoryMetrics(); + jvmMemoryMetricSet.bindTo(metricManager); + + JvmThreadMetrics jvmThreadMetrics = new JvmThreadMetrics(); + jvmThreadMetrics.bindTo(metricManager); + } + + @Override + public PredefinedMetric getType() { + return PredefinedMetric.JVM; + } +} diff --git a/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmThreadMetrics.java b/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmThreadMetrics.java new file mode 100644 index 000000000000..b59989207630 --- /dev/null +++ b/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmThreadMetrics.java @@ -0,0 +1,87 @@ +/* + * 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.iotdb.metrics.predefined.jvm; + +import org.apache.iotdb.metrics.MetricManager; +import org.apache.iotdb.metrics.predefined.IMetricSet; +import org.apache.iotdb.metrics.utils.MetricLevel; +import org.apache.iotdb.metrics.utils.PredefinedMetric; + +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadMXBean; +import java.util.Arrays; + +/** This file is modified from io.micrometer.core.instrument.binder.jvm.JvmThreadMetrics */ +public class JvmThreadMetrics implements IMetricSet { + @Override + public void bindTo(MetricManager metricManager) { + ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); + + metricManager.getOrCreateAutoGauge( + "jvm.threads.peak.threads", + MetricLevel.IMPORTANT, + threadBean, + ThreadMXBean::getPeakThreadCount); + + metricManager.getOrCreateAutoGauge( + "jvm.threads.daemon.threads", + MetricLevel.IMPORTANT, + threadBean, + ThreadMXBean::getDaemonThreadCount); + + metricManager.getOrCreateAutoGauge( + "jvm.threads.live.threads", + MetricLevel.IMPORTANT, + threadBean, + ThreadMXBean::getThreadCount); + + try { + threadBean.getAllThreadIds(); + for (Thread.State state : Thread.State.values()) { + metricManager.getOrCreateAutoGauge( + "jvm.threads.states.threads", + MetricLevel.IMPORTANT, + threadBean, + (bean) -> getThreadStateCount(bean, state), + "state", + getStateTagValue(state)); + } + } catch (Error error) { + // An error will be thrown for unsupported operations + // e.g. SubstrateVM does not support getAllThreadIds + } + } + + // VisibleForTesting + static long getThreadStateCount(ThreadMXBean threadBean, Thread.State state) { + return Arrays.stream(threadBean.getThreadInfo(threadBean.getAllThreadIds())) + .filter(threadInfo -> threadInfo != null && threadInfo.getThreadState() == state) + .count(); + } + + private static String getStateTagValue(Thread.State state) { + return state.name().toLowerCase().replace("_", "-"); + } + + @Override + public PredefinedMetric getType() { + return PredefinedMetric.JVM; + } +} diff --git a/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/logback/LogbackMetrics.java b/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/logback/LogbackMetrics.java new file mode 100644 index 000000000000..aabcb542bd01 --- /dev/null +++ b/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/logback/LogbackMetrics.java @@ -0,0 +1,180 @@ +/* + * 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.iotdb.metrics.predefined.logback; + +import org.apache.iotdb.metrics.MetricManager; +import org.apache.iotdb.metrics.predefined.IMetricSet; +import org.apache.iotdb.metrics.type.Counter; +import org.apache.iotdb.metrics.utils.MetricLevel; +import org.apache.iotdb.metrics.utils.PredefinedMetric; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.spi.LoggerContextListener; +import ch.qos.logback.classic.turbo.TurboFilter; +import ch.qos.logback.core.spi.FilterReply; +import org.slf4j.LoggerFactory; +import org.slf4j.Marker; + +import java.util.HashMap; +import java.util.Map; + +/** This file is modified from io.micrometer.core.instrument.binder.logging.LogbackMetrics */ +public class LogbackMetrics implements IMetricSet, AutoCloseable { + static ThreadLocal ignoreMetrics = new ThreadLocal<>(); + private final LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); + private final Map metricsTurboFilters = new HashMap<>(); + + public LogbackMetrics() { + loggerContext.addListener( + new LoggerContextListener() { + @Override + public boolean isResetResistant() { + return true; + } + + @Override + public void onReset(LoggerContext context) { + // re-add turbo filter because reset clears the turbo filter list + synchronized (metricsTurboFilters) { + for (MetricsTurboFilter metricsTurboFilter : metricsTurboFilters.values()) { + loggerContext.addTurboFilter(metricsTurboFilter); + } + } + } + + @Override + public void onStart(LoggerContext context) { + // no-op + } + + @Override + public void onStop(LoggerContext context) { + // no-op + } + + @Override + public void onLevelChange(Logger logger, Level level) { + // no-op + } + }); + } + + @Override + public void bindTo(MetricManager metricManager) { + MetricsTurboFilter filter = new MetricsTurboFilter(metricManager); + synchronized (metricsTurboFilters) { + metricsTurboFilters.put(metricManager, filter); + loggerContext.addTurboFilter(filter); + } + } + + public static void ignoreMetrics(Runnable r) { + ignoreMetrics.set(true); + try { + r.run(); + } finally { + ignoreMetrics.remove(); + } + } + + @Override + public PredefinedMetric getType() { + return PredefinedMetric.LOGBACK; + } + + @Override + public void close() throws Exception { + synchronized (metricsTurboFilters) { + for (MetricsTurboFilter metricsTurboFilter : metricsTurboFilters.values()) { + loggerContext.getTurboFilterList().remove(metricsTurboFilter); + } + } + } +} + +class MetricsTurboFilter extends TurboFilter { + private Counter errorCounter; + private Counter warnCounter; + private Counter infoCounter; + private Counter debugCounter; + private Counter traceCounter; + + MetricsTurboFilter(MetricManager metricManager) { + errorCounter = + metricManager.getOrCreateCounter("logback.events", MetricLevel.IMPORTANT, "level", "error"); + + warnCounter = + metricManager.getOrCreateCounter("logback.events", MetricLevel.IMPORTANT, "level", "warn"); + + infoCounter = + metricManager.getOrCreateCounter("logback.events", MetricLevel.IMPORTANT, "level", "info"); + + debugCounter = + metricManager.getOrCreateCounter("logback.events", MetricLevel.IMPORTANT, "level", "debug"); + + traceCounter = + metricManager.getOrCreateCounter("logback.events", MetricLevel.IMPORTANT, "level", "trace"); + } + + @Override + public FilterReply decide( + Marker marker, Logger logger, Level level, String format, Object[] params, Throwable t) { + // When filter is asked for decision for an isDebugEnabled call or similar test, there is no + // message (ie format) + // and no intention to log anything with this call. We will not increment counters and can + // return immediately and + // avoid the relatively expensive ThreadLocal access below. See also logbacks + // Logger.callTurboFilters(). + if (format == null) { + return FilterReply.NEUTRAL; + } + + Boolean ignored = LogbackMetrics.ignoreMetrics.get(); + if (ignored != null && ignored) { + return FilterReply.NEUTRAL; + } + + // cannot use logger.isEnabledFor(level), as it would cause a StackOverflowError by calling this + // filter again! + if (level.isGreaterOrEqual(logger.getEffectiveLevel())) { + switch (level.toInt()) { + case Level.ERROR_INT: + errorCounter.inc(); + break; + case Level.WARN_INT: + warnCounter.inc(); + break; + case Level.INFO_INT: + infoCounter.inc(); + break; + case Level.DEBUG_INT: + debugCounter.inc(); + break; + case Level.TRACE_INT: + traceCounter.inc(); + break; + } + } + + return FilterReply.NEUTRAL; + } +} diff --git a/metrics/interface/src/main/java/org/apache/iotdb/metrics/utils/JvmUtils.java b/metrics/interface/src/main/java/org/apache/iotdb/metrics/utils/JvmUtils.java new file mode 100644 index 000000000000..29f6e006808b --- /dev/null +++ b/metrics/interface/src/main/java/org/apache/iotdb/metrics/utils/JvmUtils.java @@ -0,0 +1,46 @@ +/* + * 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.iotdb.metrics.utils; + +import java.lang.management.MemoryPoolMXBean; +import java.lang.management.MemoryUsage; +import java.util.function.ToLongFunction; + +public class JvmUtils { + public static double getUsageValue( + MemoryPoolMXBean memoryPoolMXBean, ToLongFunction getter) { + MemoryUsage usage = getUsage(memoryPoolMXBean); + if (usage == null) { + return Double.NaN; + } + return getter.applyAsLong(usage); + } + + private static MemoryUsage getUsage(MemoryPoolMXBean memoryPoolMXBean) { + try { + return memoryPoolMXBean.getUsage(); + } catch (InternalError e) { + // Defensive for potential InternalError with some specific JVM options. Based on its Javadoc, + // MemoryPoolMXBean.getUsage() should return null, not throwing InternalError, so it seems to + // be a JVM bug. + return null; + } + } +} diff --git a/metrics/interface/src/main/java/org/apache/iotdb/metrics/utils/PredefinedMetric.java b/metrics/interface/src/main/java/org/apache/iotdb/metrics/utils/PredefinedMetric.java index e4b0cbdc8bef..e9484eb7aa24 100644 --- a/metrics/interface/src/main/java/org/apache/iotdb/metrics/utils/PredefinedMetric.java +++ b/metrics/interface/src/main/java/org/apache/iotdb/metrics/utils/PredefinedMetric.java @@ -21,7 +21,10 @@ public enum PredefinedMetric { JVM, - LOGBACK; + LOGBACK, + FILE, + PROCESS, + SYSTEM; @Override public String toString() { diff --git a/metrics/micrometer-metrics/src/main/java/org/apache/iotdb/metrics/micrometer/MicrometerMetricManager.java b/metrics/micrometer-metrics/src/main/java/org/apache/iotdb/metrics/micrometer/MicrometerMetricManager.java index af0149445a12..860fa88b55de 100644 --- a/metrics/micrometer-metrics/src/main/java/org/apache/iotdb/metrics/micrometer/MicrometerMetricManager.java +++ b/metrics/micrometer-metrics/src/main/java/org/apache/iotdb/metrics/micrometer/MicrometerMetricManager.java @@ -36,19 +36,11 @@ import org.apache.iotdb.metrics.type.Rate; import org.apache.iotdb.metrics.type.Timer; import org.apache.iotdb.metrics.utils.MetricLevel; -import org.apache.iotdb.metrics.utils.PredefinedMetric; import io.micrometer.core.instrument.Meter; import io.micrometer.core.instrument.Metrics; import io.micrometer.core.instrument.Tag; import io.micrometer.core.instrument.Tags; -import io.micrometer.core.instrument.binder.jvm.ClassLoaderMetrics; -import io.micrometer.core.instrument.binder.jvm.JvmCompilationMetrics; -import io.micrometer.core.instrument.binder.jvm.JvmGcMetrics; -import io.micrometer.core.instrument.binder.jvm.JvmHeapPressureMetrics; -import io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics; -import io.micrometer.core.instrument.binder.jvm.JvmThreadMetrics; -import io.micrometer.core.instrument.binder.logging.LogbackMetrics; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -390,49 +382,6 @@ private Map getMetricByType(Meter.Type type) { return metricMap; } - @Override - public void enablePredefinedMetric(PredefinedMetric metric) { - if (!isEnable) { - return; - } - switch (metric) { - case JVM: - enableJvmMetrics(); - break; - case LOGBACK: - enableLogbackMetrics(); - break; - default: - logger.warn("Unsupported metric type {}", metric); - } - } - - /** bind default metric to registry(or reporter */ - private void enableJvmMetrics() { - if (!isEnable()) { - return; - } - ClassLoaderMetrics classLoaderMetrics = new ClassLoaderMetrics(); - classLoaderMetrics.bindTo(meterRegistry); - JvmCompilationMetrics jvmCompilationMetrics = new JvmCompilationMetrics(); - jvmCompilationMetrics.bindTo(meterRegistry); - JvmGcMetrics jvmGcMetrics = new JvmGcMetrics(); - JvmHeapPressureMetrics jvmHeapPressureMetrics = new JvmHeapPressureMetrics(); - jvmGcMetrics.bindTo(meterRegistry); - jvmHeapPressureMetrics.bindTo(meterRegistry); - JvmMemoryMetrics jvmMemoryMetrics = new JvmMemoryMetrics(); - jvmMemoryMetrics.bindTo(meterRegistry); - JvmThreadMetrics jvmThreadMetrics = new JvmThreadMetrics(); - jvmThreadMetrics.bindTo(meterRegistry); - } - - private void enableLogbackMetrics() { - if (!isEnable()) { - return; - } - new LogbackMetrics().bindTo(meterRegistry); - } - @Override public void removeCounter(String metric, String... tags) { if (!isEnable()) { diff --git a/server/src/main/java/org/apache/iotdb/db/engine/cache/ChunkCache.java b/server/src/main/java/org/apache/iotdb/db/engine/cache/ChunkCache.java index 0ee70f29a27d..a32f301d784e 100644 --- a/server/src/main/java/org/apache/iotdb/db/engine/cache/ChunkCache.java +++ b/server/src/main/java/org/apache/iotdb/db/engine/cache/ChunkCache.java @@ -23,9 +23,9 @@ import org.apache.iotdb.db.conf.IoTDBConfig; import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.query.control.FileReaderManager; -import org.apache.iotdb.db.service.metrics.Metric; import org.apache.iotdb.db.service.metrics.MetricsService; -import org.apache.iotdb.db.service.metrics.Tag; +import org.apache.iotdb.db.service.metrics.enums.Metric; +import org.apache.iotdb.db.service.metrics.enums.Tag; import org.apache.iotdb.metrics.config.MetricConfigDescriptor; import org.apache.iotdb.metrics.utils.MetricLevel; import org.apache.iotdb.tsfile.file.metadata.ChunkMetadata; diff --git a/server/src/main/java/org/apache/iotdb/db/engine/cache/TimeSeriesMetadataCache.java b/server/src/main/java/org/apache/iotdb/db/engine/cache/TimeSeriesMetadataCache.java index 59f756e06864..7f10ce7df6ec 100644 --- a/server/src/main/java/org/apache/iotdb/db/engine/cache/TimeSeriesMetadataCache.java +++ b/server/src/main/java/org/apache/iotdb/db/engine/cache/TimeSeriesMetadataCache.java @@ -24,9 +24,9 @@ import org.apache.iotdb.db.conf.IoTDBConfig; import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.query.control.FileReaderManager; -import org.apache.iotdb.db.service.metrics.Metric; import org.apache.iotdb.db.service.metrics.MetricsService; -import org.apache.iotdb.db.service.metrics.Tag; +import org.apache.iotdb.db.service.metrics.enums.Metric; +import org.apache.iotdb.db.service.metrics.enums.Tag; import org.apache.iotdb.metrics.config.MetricConfigDescriptor; import org.apache.iotdb.metrics.utils.MetricLevel; import org.apache.iotdb.tsfile.file.metadata.ChunkMetadata; diff --git a/server/src/main/java/org/apache/iotdb/db/engine/compaction/CompactionMetricsManager.java b/server/src/main/java/org/apache/iotdb/db/engine/compaction/CompactionMetricsManager.java index f852dd9d61d2..9748cd5ccf09 100644 --- a/server/src/main/java/org/apache/iotdb/db/engine/compaction/CompactionMetricsManager.java +++ b/server/src/main/java/org/apache/iotdb/db/engine/compaction/CompactionMetricsManager.java @@ -24,9 +24,9 @@ import org.apache.iotdb.db.engine.compaction.cross.CrossSpaceCompactionTask; import org.apache.iotdb.db.engine.compaction.inner.InnerSpaceCompactionTask; import org.apache.iotdb.db.engine.compaction.task.AbstractCompactionTask; -import org.apache.iotdb.db.service.metrics.Metric; import org.apache.iotdb.db.service.metrics.MetricsService; -import org.apache.iotdb.db.service.metrics.Tag; +import org.apache.iotdb.db.service.metrics.enums.Metric; +import org.apache.iotdb.db.service.metrics.enums.Tag; import org.apache.iotdb.metrics.config.MetricConfigDescriptor; import org.apache.iotdb.metrics.utils.MetricLevel; diff --git a/server/src/main/java/org/apache/iotdb/db/engine/flush/FlushManager.java b/server/src/main/java/org/apache/iotdb/db/engine/flush/FlushManager.java index 278229d00b9d..d0455e0a7aa0 100644 --- a/server/src/main/java/org/apache/iotdb/db/engine/flush/FlushManager.java +++ b/server/src/main/java/org/apache/iotdb/db/engine/flush/FlushManager.java @@ -29,9 +29,9 @@ import org.apache.iotdb.db.engine.flush.pool.FlushTaskPoolManager; import org.apache.iotdb.db.engine.storagegroup.TsFileProcessor; import org.apache.iotdb.db.rescon.AbstractPoolManager; -import org.apache.iotdb.db.service.metrics.Metric; import org.apache.iotdb.db.service.metrics.MetricsService; -import org.apache.iotdb.db.service.metrics.Tag; +import org.apache.iotdb.db.service.metrics.enums.Metric; +import org.apache.iotdb.db.service.metrics.enums.Tag; import org.apache.iotdb.metrics.config.MetricConfigDescriptor; import org.apache.iotdb.metrics.utils.MetricLevel; diff --git a/server/src/main/java/org/apache/iotdb/db/engine/flush/MemTableFlushTask.java b/server/src/main/java/org/apache/iotdb/db/engine/flush/MemTableFlushTask.java index 387c609e3d97..5c7fa5e62d32 100644 --- a/server/src/main/java/org/apache/iotdb/db/engine/flush/MemTableFlushTask.java +++ b/server/src/main/java/org/apache/iotdb/db/engine/flush/MemTableFlushTask.java @@ -27,9 +27,9 @@ import org.apache.iotdb.db.exception.runtime.FlushRunTimeException; import org.apache.iotdb.db.metadata.idtable.entry.IDeviceID; import org.apache.iotdb.db.rescon.SystemInfo; -import org.apache.iotdb.db.service.metrics.Metric; import org.apache.iotdb.db.service.metrics.MetricsService; -import org.apache.iotdb.db.service.metrics.Tag; +import org.apache.iotdb.db.service.metrics.enums.Metric; +import org.apache.iotdb.db.service.metrics.enums.Tag; import org.apache.iotdb.metrics.config.MetricConfigDescriptor; import org.apache.iotdb.metrics.utils.MetricLevel; import org.apache.iotdb.tsfile.write.chunk.IChunkWriter; diff --git a/server/src/main/java/org/apache/iotdb/db/engine/memtable/AbstractMemTable.java b/server/src/main/java/org/apache/iotdb/db/engine/memtable/AbstractMemTable.java index cf045e3fb960..38bcc251dc95 100644 --- a/server/src/main/java/org/apache/iotdb/db/engine/memtable/AbstractMemTable.java +++ b/server/src/main/java/org/apache/iotdb/db/engine/memtable/AbstractMemTable.java @@ -33,9 +33,9 @@ import org.apache.iotdb.db.mpp.plan.planner.plan.node.write.InsertTabletNode; import org.apache.iotdb.db.qp.physical.crud.InsertRowPlan; import org.apache.iotdb.db.qp.physical.crud.InsertTabletPlan; -import org.apache.iotdb.db.service.metrics.Metric; import org.apache.iotdb.db.service.metrics.MetricsService; -import org.apache.iotdb.db.service.metrics.Tag; +import org.apache.iotdb.db.service.metrics.enums.Metric; +import org.apache.iotdb.db.service.metrics.enums.Tag; import org.apache.iotdb.db.utils.MemUtils; import org.apache.iotdb.db.wal.buffer.IWALByteBufferView; import org.apache.iotdb.db.wal.utils.WALWriteUtils; diff --git a/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/DataRegion.java b/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/DataRegion.java index 9fbc91165539..e897ae4856b0 100755 --- a/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/DataRegion.java +++ b/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/DataRegion.java @@ -75,9 +75,9 @@ import org.apache.iotdb.db.rescon.TsFileResourceManager; import org.apache.iotdb.db.service.IoTDB; import org.apache.iotdb.db.service.SettleService; -import org.apache.iotdb.db.service.metrics.Metric; import org.apache.iotdb.db.service.metrics.MetricsService; -import org.apache.iotdb.db.service.metrics.Tag; +import org.apache.iotdb.db.service.metrics.enums.Metric; +import org.apache.iotdb.db.service.metrics.enums.Tag; import org.apache.iotdb.db.sync.sender.manager.TsFileSyncManager; import org.apache.iotdb.db.tools.settle.TsFileAndModSettleTool; import org.apache.iotdb.db.utils.CopyOnReadLinkedList; diff --git a/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/TsFileProcessorInfo.java b/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/TsFileProcessorInfo.java index ae9b885fe51f..71298ca5e4de 100644 --- a/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/TsFileProcessorInfo.java +++ b/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/TsFileProcessorInfo.java @@ -18,9 +18,9 @@ */ package org.apache.iotdb.db.engine.storagegroup; -import org.apache.iotdb.db.service.metrics.Metric; import org.apache.iotdb.db.service.metrics.MetricsService; -import org.apache.iotdb.db.service.metrics.Tag; +import org.apache.iotdb.db.service.metrics.enums.Metric; +import org.apache.iotdb.db.service.metrics.enums.Tag; import org.apache.iotdb.metrics.config.MetricConfigDescriptor; import org.apache.iotdb.metrics.utils.MetricLevel; diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/rescon/TimeseriesStatistics.java b/server/src/main/java/org/apache/iotdb/db/metadata/rescon/TimeseriesStatistics.java index 75b91051747d..097d1accce29 100644 --- a/server/src/main/java/org/apache/iotdb/db/metadata/rescon/TimeseriesStatistics.java +++ b/server/src/main/java/org/apache/iotdb/db/metadata/rescon/TimeseriesStatistics.java @@ -18,9 +18,9 @@ */ package org.apache.iotdb.db.metadata.rescon; -import org.apache.iotdb.db.service.metrics.Metric; import org.apache.iotdb.db.service.metrics.MetricsService; -import org.apache.iotdb.db.service.metrics.Tag; +import org.apache.iotdb.db.service.metrics.enums.Metric; +import org.apache.iotdb.db.service.metrics.enums.Tag; import org.apache.iotdb.metrics.config.MetricConfigDescriptor; import org.apache.iotdb.metrics.utils.MetricLevel; diff --git a/server/src/main/java/org/apache/iotdb/db/query/pool/RawQueryReadTaskPoolManager.java b/server/src/main/java/org/apache/iotdb/db/query/pool/RawQueryReadTaskPoolManager.java index 6d95727329c1..1a1301f1307e 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/pool/RawQueryReadTaskPoolManager.java +++ b/server/src/main/java/org/apache/iotdb/db/query/pool/RawQueryReadTaskPoolManager.java @@ -23,9 +23,9 @@ import org.apache.iotdb.commons.concurrent.ThreadName; import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.rescon.AbstractPoolManager; -import org.apache.iotdb.db.service.metrics.Metric; import org.apache.iotdb.db.service.metrics.MetricsService; -import org.apache.iotdb.db.service.metrics.Tag; +import org.apache.iotdb.db.service.metrics.enums.Metric; +import org.apache.iotdb.db.service.metrics.enums.Tag; import org.apache.iotdb.metrics.config.MetricConfigDescriptor; import org.apache.iotdb.metrics.utils.MetricLevel; diff --git a/server/src/main/java/org/apache/iotdb/db/service/metrics/MetricsService.java b/server/src/main/java/org/apache/iotdb/db/service/metrics/MetricsService.java index 2e8579e568ce..74228c1ae6f6 100644 --- a/server/src/main/java/org/apache/iotdb/db/service/metrics/MetricsService.java +++ b/server/src/main/java/org/apache/iotdb/db/service/metrics/MetricsService.java @@ -24,19 +24,19 @@ import org.apache.iotdb.commons.service.IService; import org.apache.iotdb.commons.service.JMXService; import org.apache.iotdb.commons.service.ServiceType; -import org.apache.iotdb.commons.utils.FileUtils; -import org.apache.iotdb.db.conf.IoTDBDescriptor; -import org.apache.iotdb.db.wal.node.WALNode; +import org.apache.iotdb.db.service.metrics.predefined.FileMetrics; +import org.apache.iotdb.db.service.metrics.predefined.ProcessMetrics; +import org.apache.iotdb.db.service.metrics.predefined.SystemMetrics; import org.apache.iotdb.metrics.MetricService; import org.apache.iotdb.metrics.config.ReloadLevel; -import org.apache.iotdb.metrics.utils.MetricLevel; +import org.apache.iotdb.metrics.predefined.IMetricSet; +import org.apache.iotdb.metrics.predefined.jvm.JvmMetrics; +import org.apache.iotdb.metrics.predefined.logback.LogbackMetrics; +import org.apache.iotdb.metrics.utils.PredefinedMetric; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; -import java.util.stream.Stream; - public class MetricsService extends MetricService implements MetricsServiceMBean, IService { private static final Logger logger = LoggerFactory.getLogger(MetricsService.class); private final String mbeanName = @@ -77,119 +77,29 @@ public void restartService() throws StartupException { } @Override - public void collectFileSystemInfo() { - logger.info("start collecting fileSize and fileCount of wal/seq/unseq"); - String[] walDirs = IoTDBDescriptor.getInstance().getConfig().getWalDirs(); - metricManager.getOrCreateAutoGauge( - Metric.FILE_SIZE.toString(), - MetricLevel.IMPORTANT, - walDirs, - value -> Stream.of(value).mapToLong(dir -> FileUtils.getDirSize(dir)).sum(), - Tag.NAME.toString(), - "wal"); - - String[] dataDirs = IoTDBDescriptor.getInstance().getConfig().getDataDirs(); - metricManager.getOrCreateAutoGauge( - Metric.FILE_SIZE.toString(), - MetricLevel.IMPORTANT, - dataDirs, - value -> - Stream.of(value) - .mapToLong( - dir -> { - dir += File.separator + IoTDBConstant.SEQUENCE_FLODER_NAME; - return FileUtils.getDirSize(dir); - }) - .sum(), - Tag.NAME.toString(), - "seq"); - metricManager.getOrCreateAutoGauge( - Metric.FILE_SIZE.toString(), - MetricLevel.IMPORTANT, - dataDirs, - value -> - Stream.of(value) - .mapToLong( - dir -> { - dir += File.separator + IoTDBConstant.UNSEQUENCE_FLODER_NAME; - return FileUtils.getDirSize(dir); - }) - .sum(), - Tag.NAME.toString(), - "unseq"); - metricManager.getOrCreateAutoGauge( - Metric.FILE_COUNT.toString(), - MetricLevel.IMPORTANT, - walDirs, - value -> - Stream.of(value) - .mapToLong( - dir -> { - File walFolder = new File(dir); - File[] walNodeFolders = walFolder.listFiles(WALNode::walNodeFolderNameFilter); - for (File walNodeFolder : walNodeFolders) { - if (walNodeFolder.exists() && walNodeFolder.isDirectory()) { - return org.apache.commons.io.FileUtils.listFiles( - walNodeFolder, null, true) - .size(); - } - } - return 0L; - }) - .sum(), - Tag.NAME.toString(), - "wal"); - metricManager.getOrCreateAutoGauge( - Metric.FILE_COUNT.toString(), - MetricLevel.IMPORTANT, - dataDirs, - value -> - Stream.of(value) - .mapToLong( - dir -> { - dir += File.separator + IoTDBConstant.SEQUENCE_FLODER_NAME; - return org.apache.commons.io.FileUtils.listFiles( - new File(dir), new String[] {"tsfile"}, true) - .size(); - }) - .sum(), - Tag.NAME.toString(), - "seq"); - metricManager.getOrCreateAutoGauge( - Metric.FILE_COUNT.toString(), - MetricLevel.IMPORTANT, - dataDirs, - value -> - Stream.of(value) - .mapToLong( - dir -> { - dir += File.separator + IoTDBConstant.UNSEQUENCE_FLODER_NAME; - return org.apache.commons.io.FileUtils.listFiles( - new File(dir), new String[] {"tsfile"}, true) - .size(); - }) - .sum(), - Tag.NAME.toString(), - "unseq"); - } - - @Override - protected void collectProcessInfo() { - logger.info("start collecting information of metric service's process"); - ProcessMetricsMonitor processMetricsMonitor = ProcessMetricsMonitor.getInstance(); - processMetricsMonitor.collectProcessCPUInfo(); - processMetricsMonitor.collectProcessMemInfo(); - processMetricsMonitor.collectThreadInfo(); - processMetricsMonitor.collectProcessStatusInfo(); - } - - @Override - protected void collectSystemInfo() { - logger.info("start collecting information of system hardware"); - SysRunMetricsMonitor sysRunMetricsMonitor = SysRunMetricsMonitor.getInstance(); - sysRunMetricsMonitor.collectSystemCpuInfo(); - sysRunMetricsMonitor.collectSystemMEMInfo(); - sysRunMetricsMonitor.collectSystemDiskInfo(); + public void enablePredefinedMetric(PredefinedMetric metric) { + IMetricSet metricSet; + switch (metric) { + case JVM: + metricSet = new JvmMetrics(); + break; + case LOGBACK: + metricSet = new LogbackMetrics(); + break; + case FILE: + metricSet = new FileMetrics(); + break; + case PROCESS: + metricSet = new ProcessMetrics(); + break; + case SYSTEM: + metricSet = new SystemMetrics(); + break; + default: + logger.error("Unknown predefined metrics: {}", metric); + return; + } + metricSet.bindTo(metricManager); } @Override diff --git a/server/src/main/java/org/apache/iotdb/db/service/metrics/Metric.java b/server/src/main/java/org/apache/iotdb/db/service/metrics/enums/Metric.java similarity index 96% rename from server/src/main/java/org/apache/iotdb/db/service/metrics/Metric.java rename to server/src/main/java/org/apache/iotdb/db/service/metrics/enums/Metric.java index ca6b16844faf..79c96b6fd65a 100644 --- a/server/src/main/java/org/apache/iotdb/db/service/metrics/Metric.java +++ b/server/src/main/java/org/apache/iotdb/db/service/metrics/enums/Metric.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.service.metrics; +package org.apache.iotdb.db.service.metrics.enums; public enum Metric { ENTRY, diff --git a/server/src/main/java/org/apache/iotdb/db/service/metrics/Operation.java b/server/src/main/java/org/apache/iotdb/db/service/metrics/enums/Operation.java similarity index 96% rename from server/src/main/java/org/apache/iotdb/db/service/metrics/Operation.java rename to server/src/main/java/org/apache/iotdb/db/service/metrics/enums/Operation.java index 4b8f3db666ac..365c74be4cff 100644 --- a/server/src/main/java/org/apache/iotdb/db/service/metrics/Operation.java +++ b/server/src/main/java/org/apache/iotdb/db/service/metrics/enums/Operation.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.iotdb.db.service.metrics; +package org.apache.iotdb.db.service.metrics.enums; public enum Operation { EXECUTE_JDBC_BATCH("EXECUTE_JDBC_BATCH"), diff --git a/server/src/main/java/org/apache/iotdb/db/service/metrics/Tag.java b/server/src/main/java/org/apache/iotdb/db/service/metrics/enums/Tag.java similarity index 94% rename from server/src/main/java/org/apache/iotdb/db/service/metrics/Tag.java rename to server/src/main/java/org/apache/iotdb/db/service/metrics/enums/Tag.java index 67d387200e8a..cb2db3a578fc 100644 --- a/server/src/main/java/org/apache/iotdb/db/service/metrics/Tag.java +++ b/server/src/main/java/org/apache/iotdb/db/service/metrics/enums/Tag.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.service.metrics; +package org.apache.iotdb.db.service.metrics.enums; public enum Tag { TYPE, diff --git a/server/src/main/java/org/apache/iotdb/db/service/metrics/predefined/FileMetrics.java b/server/src/main/java/org/apache/iotdb/db/service/metrics/predefined/FileMetrics.java new file mode 100644 index 000000000000..0cf0c19879a4 --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/service/metrics/predefined/FileMetrics.java @@ -0,0 +1,137 @@ +/* + * 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.iotdb.db.service.metrics.predefined; + +import org.apache.iotdb.commons.conf.IoTDBConstant; +import org.apache.iotdb.commons.utils.FileUtils; +import org.apache.iotdb.db.conf.IoTDBDescriptor; +import org.apache.iotdb.db.service.metrics.enums.Metric; +import org.apache.iotdb.db.service.metrics.enums.Tag; +import org.apache.iotdb.db.wal.node.WALNode; +import org.apache.iotdb.metrics.MetricManager; +import org.apache.iotdb.metrics.predefined.IMetricSet; +import org.apache.iotdb.metrics.utils.MetricLevel; +import org.apache.iotdb.metrics.utils.PredefinedMetric; + +import java.io.File; +import java.util.stream.Stream; + +public class FileMetrics implements IMetricSet { + @Override + public void bindTo(MetricManager metricManager) { + String[] walDirs = IoTDBDescriptor.getInstance().getConfig().getWalDirs(); + metricManager.getOrCreateAutoGauge( + Metric.FILE_SIZE.toString(), + MetricLevel.IMPORTANT, + walDirs, + value -> Stream.of(value).mapToLong(dir -> FileUtils.getDirSize(dir)).sum(), + Tag.NAME.toString(), + "wal"); + + String[] dataDirs = IoTDBDescriptor.getInstance().getConfig().getDataDirs(); + metricManager.getOrCreateAutoGauge( + Metric.FILE_SIZE.toString(), + MetricLevel.IMPORTANT, + dataDirs, + value -> + Stream.of(value) + .mapToLong( + dir -> { + dir += File.separator + IoTDBConstant.SEQUENCE_FLODER_NAME; + return FileUtils.getDirSize(dir); + }) + .sum(), + Tag.NAME.toString(), + "seq"); + metricManager.getOrCreateAutoGauge( + Metric.FILE_SIZE.toString(), + MetricLevel.IMPORTANT, + dataDirs, + value -> + Stream.of(value) + .mapToLong( + dir -> { + dir += File.separator + IoTDBConstant.UNSEQUENCE_FLODER_NAME; + return FileUtils.getDirSize(dir); + }) + .sum(), + Tag.NAME.toString(), + "unseq"); + metricManager.getOrCreateAutoGauge( + Metric.FILE_COUNT.toString(), + MetricLevel.IMPORTANT, + walDirs, + value -> + Stream.of(value) + .mapToLong( + dir -> { + File walFolder = new File(dir); + File[] walNodeFolders = walFolder.listFiles(WALNode::walNodeFolderNameFilter); + for (File walNodeFolder : walNodeFolders) { + if (walNodeFolder.exists() && walNodeFolder.isDirectory()) { + return org.apache.commons.io.FileUtils.listFiles( + walNodeFolder, null, true) + .size(); + } + } + return 0L; + }) + .sum(), + Tag.NAME.toString(), + "wal"); + metricManager.getOrCreateAutoGauge( + Metric.FILE_COUNT.toString(), + MetricLevel.IMPORTANT, + dataDirs, + value -> + Stream.of(value) + .mapToLong( + dir -> { + dir += File.separator + IoTDBConstant.SEQUENCE_FLODER_NAME; + return org.apache.commons.io.FileUtils.listFiles( + new File(dir), new String[] {"tsfile"}, true) + .size(); + }) + .sum(), + Tag.NAME.toString(), + "seq"); + metricManager.getOrCreateAutoGauge( + Metric.FILE_COUNT.toString(), + MetricLevel.IMPORTANT, + dataDirs, + value -> + Stream.of(value) + .mapToLong( + dir -> { + dir += File.separator + IoTDBConstant.UNSEQUENCE_FLODER_NAME; + return org.apache.commons.io.FileUtils.listFiles( + new File(dir), new String[] {"tsfile"}, true) + .size(); + }) + .sum(), + Tag.NAME.toString(), + "unseq"); + } + + @Override + public PredefinedMetric getType() { + return PredefinedMetric.FILE; + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/service/metrics/ProcessMetricsMonitor.java b/server/src/main/java/org/apache/iotdb/db/service/metrics/predefined/ProcessMetrics.java similarity index 80% rename from server/src/main/java/org/apache/iotdb/db/service/metrics/ProcessMetricsMonitor.java rename to server/src/main/java/org/apache/iotdb/db/service/metrics/predefined/ProcessMetrics.java index 3e91f605b0b6..949a31b35602 100644 --- a/server/src/main/java/org/apache/iotdb/db/service/metrics/ProcessMetricsMonitor.java +++ b/server/src/main/java/org/apache/iotdb/db/service/metrics/predefined/ProcessMetrics.java @@ -17,22 +17,43 @@ * under the License. */ -package org.apache.iotdb.db.service.metrics; +package org.apache.iotdb.db.service.metrics.predefined; +import org.apache.iotdb.db.service.metrics.enums.Metric; +import org.apache.iotdb.db.service.metrics.enums.Tag; import org.apache.iotdb.metrics.MetricManager; +import org.apache.iotdb.metrics.predefined.IMetricSet; import org.apache.iotdb.metrics.utils.MetricLevel; +import org.apache.iotdb.metrics.utils.PredefinedMetric; import com.sun.management.OperatingSystemMXBean; import java.lang.management.ManagementFactory; -public class ProcessMetricsMonitor { - - private MetricManager metricManager = MetricsService.getInstance().getMetricManager(); +public class ProcessMetrics implements IMetricSet { private OperatingSystemMXBean sunOsMXBean; private Runtime runtime; - public void collectProcessCPUInfo() { + public ProcessMetrics() { + sunOsMXBean = + (com.sun.management.OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean(); + runtime = Runtime.getRuntime(); + } + + @Override + public void bindTo(MetricManager metricManager) { + collectProcessCPUInfo(metricManager); + collectProcessMemInfo(metricManager); + collectProcessStatusInfo(metricManager); + collectThreadInfo(metricManager); + } + + @Override + public PredefinedMetric getType() { + return PredefinedMetric.PROCESS; + } + + private void collectProcessCPUInfo(MetricManager metricManager) { metricManager.getOrCreateAutoGauge( Metric.PROCESS_CPU_LOAD.toString(), MetricLevel.CORE, @@ -50,7 +71,7 @@ public void collectProcessCPUInfo() { "process"); } - public void collectProcessMemInfo() { + private void collectProcessMemInfo(MetricManager metricManager) { Runtime runtime = Runtime.getRuntime(); metricManager.getOrCreateAutoGauge( Metric.PROCESS_MAX_MEM.toString(), @@ -89,7 +110,7 @@ public void collectProcessMemInfo() { "process"); } - public void collectThreadInfo() { + private void collectThreadInfo(MetricManager metricManager) { metricManager.getOrCreateAutoGauge( Metric.PROCESS_THREADS_COUNT.toString(), MetricLevel.CORE, @@ -99,7 +120,7 @@ public void collectThreadInfo() { "process"); } - public void collectProcessStatusInfo() { + private void collectProcessStatusInfo(MetricManager metricManager) { metricManager.getOrCreateAutoGauge( Metric.PROCESS_STATUS.toString(), MetricLevel.CORE, @@ -109,12 +130,6 @@ public void collectProcessStatusInfo() { "process"); } - private ProcessMetricsMonitor() { - sunOsMXBean = - (com.sun.management.OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean(); - runtime = Runtime.getRuntime(); - } - private long getProcessUsedMemory() { return runtime.totalMemory() - runtime.freeMemory(); } @@ -137,12 +152,4 @@ private double getProcessMemoryRatio() { long totalPhysicalMemorySize = sunOsMXBean.getTotalPhysicalMemorySize(); return (double) processUsedMemory / (double) totalPhysicalMemorySize * 100; } - - public static ProcessMetricsMonitor getInstance() { - return ThreadMetricsMonitorHolder.INSTANCE; - } - - private static class ThreadMetricsMonitorHolder { - private static final ProcessMetricsMonitor INSTANCE = new ProcessMetricsMonitor(); - } } diff --git a/server/src/main/java/org/apache/iotdb/db/service/metrics/SysRunMetricsMonitor.java b/server/src/main/java/org/apache/iotdb/db/service/metrics/predefined/SystemMetrics.java similarity index 82% rename from server/src/main/java/org/apache/iotdb/db/service/metrics/SysRunMetricsMonitor.java rename to server/src/main/java/org/apache/iotdb/db/service/metrics/predefined/SystemMetrics.java index e7ccea7450bd..4069a5afc351 100644 --- a/server/src/main/java/org/apache/iotdb/db/service/metrics/SysRunMetricsMonitor.java +++ b/server/src/main/java/org/apache/iotdb/db/service/metrics/predefined/SystemMetrics.java @@ -16,26 +16,41 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.iotdb.db.service.metrics; +package org.apache.iotdb.db.service.metrics.predefined; import org.apache.iotdb.db.conf.IoTDBDescriptor; +import org.apache.iotdb.db.service.metrics.enums.Metric; +import org.apache.iotdb.db.service.metrics.enums.Tag; import org.apache.iotdb.metrics.MetricManager; +import org.apache.iotdb.metrics.predefined.IMetricSet; import org.apache.iotdb.metrics.utils.MetricLevel; +import org.apache.iotdb.metrics.utils.PredefinedMetric; import com.sun.management.OperatingSystemMXBean; import java.io.File; import java.lang.management.ManagementFactory; -public class SysRunMetricsMonitor { - private MetricManager metricManager = MetricsService.getInstance().getMetricManager(); +public class SystemMetrics implements IMetricSet { private com.sun.management.OperatingSystemMXBean osMXBean; - private SysRunMetricsMonitor() { + public SystemMetrics() { osMXBean = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean(); } - public void collectSystemCpuInfo() { + @Override + public void bindTo(MetricManager metricManager) { + collectSystemCpuInfo(metricManager); + collectSystemDiskInfo(metricManager); + collectSystemMEMInfo(metricManager); + } + + @Override + public PredefinedMetric getType() { + return PredefinedMetric.SYSTEM; + } + + private void collectSystemCpuInfo(MetricManager metricManager) { metricManager.getOrCreateAutoGauge( Metric.SYS_CPU_LOAD.toString(), MetricLevel.CORE, @@ -50,7 +65,7 @@ public void collectSystemCpuInfo() { .set(osMXBean.getAvailableProcessors()); } - public void collectSystemMEMInfo() { + private void collectSystemMEMInfo(MetricManager metricManager) { metricManager .getOrCreateGauge( Metric.SYS_TOTAL_PHYSICAL_MEMORY_SIZE.toString(), @@ -88,7 +103,7 @@ public void collectSystemMEMInfo() { "system"); } - public void collectSystemDiskInfo() { + private void collectSystemDiskInfo(MetricManager metricManager) { metricManager.getOrCreateAutoGauge( Metric.SYS_DISK_TOTAL_SPACE.toString(), MetricLevel.CORE, @@ -123,12 +138,4 @@ private long getSysDickFreeSpace() { } return sysFreeSpace; } - - public static SysRunMetricsMonitor getInstance() { - return SysRunMetricsMonitor.SysRunMetricsMonitorHolder.INSTANCE; - } - - private static class SysRunMetricsMonitorHolder { - private static final SysRunMetricsMonitor INSTANCE = new SysRunMetricsMonitor(); - } } diff --git a/server/src/main/java/org/apache/iotdb/db/service/thrift/ProcessorWithMetrics.java b/server/src/main/java/org/apache/iotdb/db/service/thrift/ProcessorWithMetrics.java index 4194b46fff8d..e9e08d80b238 100644 --- a/server/src/main/java/org/apache/iotdb/db/service/thrift/ProcessorWithMetrics.java +++ b/server/src/main/java/org/apache/iotdb/db/service/thrift/ProcessorWithMetrics.java @@ -19,9 +19,9 @@ package org.apache.iotdb.db.service.thrift; -import org.apache.iotdb.db.service.metrics.Metric; import org.apache.iotdb.db.service.metrics.MetricsService; -import org.apache.iotdb.db.service.metrics.Tag; +import org.apache.iotdb.db.service.metrics.enums.Metric; +import org.apache.iotdb.db.service.metrics.enums.Tag; import org.apache.iotdb.metrics.utils.MetricLevel; import org.apache.iotdb.service.rpc.thrift.TSIService.Iface; import org.apache.iotdb.service.rpc.thrift.TSIService.Processor; diff --git a/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/DataNodeTSIServiceImpl.java b/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/DataNodeTSIServiceImpl.java index 4d403bef8d42..97ff7d907dda 100644 --- a/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/DataNodeTSIServiceImpl.java +++ b/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/DataNodeTSIServiceImpl.java @@ -51,7 +51,7 @@ import org.apache.iotdb.db.query.control.SessionTimeoutManager; import org.apache.iotdb.db.service.basic.BasicOpenSessionResp; import org.apache.iotdb.db.service.metrics.MetricsService; -import org.apache.iotdb.db.service.metrics.Operation; +import org.apache.iotdb.db.service.metrics.enums.Operation; import org.apache.iotdb.db.utils.QueryDataSetUtils; import org.apache.iotdb.metrics.config.MetricConfigDescriptor; import org.apache.iotdb.metrics.utils.MetricLevel; diff --git a/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/TSServiceImpl.java b/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/TSServiceImpl.java index febb2e195582..88fc23165f76 100644 --- a/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/TSServiceImpl.java +++ b/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/TSServiceImpl.java @@ -72,7 +72,7 @@ import org.apache.iotdb.db.service.basic.BasicOpenSessionResp; import org.apache.iotdb.db.service.basic.ServiceProvider; import org.apache.iotdb.db.service.metrics.MetricsService; -import org.apache.iotdb.db.service.metrics.Operation; +import org.apache.iotdb.db.service.metrics.enums.Operation; import org.apache.iotdb.db.tools.watermark.GroupedLSBWatermarkEncoder; import org.apache.iotdb.db.tools.watermark.WatermarkEncoder; import org.apache.iotdb.db.utils.QueryDataSetUtils; From 271148e0729a0d9c88857a0f6315e5973d0c6db1 Mon Sep 17 00:00:00 2001 From: Jackie Tien Date: Thu, 19 May 2022 19:22:58 +0800 Subject: [PATCH 051/436] Print file name while meeting error in reading or deserializing (#5959) --- .../tsfile/read/TsFileSequenceReader.java | 52 +++++++++++++++---- 1 file changed, 41 insertions(+), 11 deletions(-) diff --git a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/TsFileSequenceReader.java b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/TsFileSequenceReader.java index 3c063d40deb2..02a226a4ef8d 100644 --- a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/TsFileSequenceReader.java +++ b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/TsFileSequenceReader.java @@ -1041,7 +1041,12 @@ public void readPlanIndex() throws IOException { * @throws IOException io error */ public ChunkHeader readChunkHeader(byte chunkType) throws IOException { - return ChunkHeader.deserializeFrom(tsFileInput.wrapAsInputStream(), chunkType); + try { + return ChunkHeader.deserializeFrom(tsFileInput.wrapAsInputStream(), chunkType); + } catch (Throwable t) { + logger.error("Exception happened while reading chunk header of {}", file, t); + throw t; + } } /** @@ -1051,7 +1056,12 @@ public ChunkHeader readChunkHeader(byte chunkType) throws IOException { * @param chunkHeaderSize the size of chunk's header */ private ChunkHeader readChunkHeader(long position, int chunkHeaderSize) throws IOException { - return ChunkHeader.deserializeFrom(tsFileInput, position, chunkHeaderSize); + try { + return ChunkHeader.deserializeFrom(tsFileInput, position, chunkHeaderSize); + } catch (Throwable t) { + logger.error("Exception happened while reading chunk header of {}", file, t); + throw t; + } } /** @@ -1062,7 +1072,12 @@ private ChunkHeader readChunkHeader(long position, int chunkHeaderSize) throws I * @return the pages of this chunk */ private ByteBuffer readChunk(long position, int dataSize) throws IOException { - return readData(position, dataSize); + try { + return readData(position, dataSize); + } catch (Throwable t) { + logger.error("Exception happened while reading chunk of {}", file, t); + throw t; + } } /** @@ -1072,12 +1087,17 @@ private ByteBuffer readChunk(long position, int dataSize) throws IOException { * @return -chunk */ public Chunk readMemChunk(ChunkMetadata metaData) throws IOException { - int chunkHeadSize = ChunkHeader.getSerializedSize(metaData.getMeasurementUid()); - ChunkHeader header = readChunkHeader(metaData.getOffsetOfChunkHeader(), chunkHeadSize); - ByteBuffer buffer = - readChunk( - metaData.getOffsetOfChunkHeader() + header.getSerializedSize(), header.getDataSize()); - return new Chunk(header, buffer, metaData.getDeleteIntervalList(), metaData.getStatistics()); + try { + int chunkHeadSize = ChunkHeader.getSerializedSize(metaData.getMeasurementUid()); + ChunkHeader header = readChunkHeader(metaData.getOffsetOfChunkHeader(), chunkHeadSize); + ByteBuffer buffer = + readChunk( + metaData.getOffsetOfChunkHeader() + header.getSerializedSize(), header.getDataSize()); + return new Chunk(header, buffer, metaData.getDeleteIntervalList(), metaData.getStatistics()); + } catch (Throwable t) { + logger.error("Exception happened while reading chunk of {}", file, t); + throw t; + } } /** @@ -1103,7 +1123,12 @@ public Chunk readMemChunk(CachedChunkLoaderImpl.ChunkCacheKey chunkCacheKey) thr * @param type given tsfile data type */ public PageHeader readPageHeader(TSDataType type, boolean hasStatistic) throws IOException { - return PageHeader.deserializeFrom(tsFileInput.wrapAsInputStream(), type, hasStatistic); + try { + return PageHeader.deserializeFrom(tsFileInput.wrapAsInputStream(), type, hasStatistic); + } catch (Throwable t) { + logger.error("Exception happened while reading page header of {}", file, t); + throw t; + } } public long position() throws IOException { @@ -1216,7 +1241,12 @@ protected ByteBuffer readData(long position, int totalSize) throws IOException { * @return data that been read. */ protected ByteBuffer readData(long start, long end) throws IOException { - return readData(start, (int) (end - start)); + try { + return readData(start, (int) (end - start)); + } catch (Throwable t) { + logger.error("Exception happened while reading data of {}", file, t); + throw t; + } } /** notice, the target bytebuffer are not flipped. */ From 854d945850f10eda42ca690135c4d91de7ba4936 Mon Sep 17 00:00:00 2001 From: lisijia <44458757+cigarl@users.noreply.github.com> Date: Thu, 19 May 2022 19:24:35 +0800 Subject: [PATCH 052/436] remove dataOutputStream (#5946) --- .../confignode/persistence/NodeInfo.java | 43 +++++++++---------- .../confignode/persistence/PartitionInfo.java | 35 ++++++++------- .../commons/partition/DataPartition.java | 28 ++++++------ .../commons/partition/SchemaPartition.java | 28 ++++++------ .../commons/partition/DataPartitionTest.java | 12 ++---- .../partition/SchemaPartitionTest.java | 12 ++---- 6 files changed, 74 insertions(+), 84 deletions(-) diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/NodeInfo.java b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/NodeInfo.java index d6ff4a43a85e..0cd49efaea13 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/NodeInfo.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/NodeInfo.java @@ -32,6 +32,7 @@ import org.apache.iotdb.confignode.consensus.request.write.RegisterDataNodeReq; import org.apache.iotdb.confignode.consensus.response.DataNodeInfosResp; import org.apache.iotdb.rpc.TSStatusCode; +import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils; import org.apache.thrift.TException; import org.apache.thrift.protocol.TBinaryProtocol; @@ -40,12 +41,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.DataInputStream; -import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -288,16 +289,15 @@ public boolean processTakeSnapshot(File snapshotDir) throws IOException, TExcept configNodeInfoReadWriteLock.readLock().lock(); dataNodeInfoReadWriteLock.readLock().lock(); try (FileOutputStream fileOutputStream = new FileOutputStream(tmpFile); - DataOutputStream dataOutputStream = new DataOutputStream(fileOutputStream); - TIOStreamTransport tioStreamTransport = new TIOStreamTransport(dataOutputStream)) { + TIOStreamTransport tioStreamTransport = new TIOStreamTransport(fileOutputStream)) { TProtocol protocol = new TBinaryProtocol(tioStreamTransport); - dataOutputStream.writeInt(nextDataNodeId.get()); + ReadWriteIOUtils.write(nextDataNodeId.get(), fileOutputStream); - serializeOnlineDataNode(dataOutputStream, protocol); + serializeOnlineDataNode(fileOutputStream, protocol); - serializeDrainingDataNodes(dataOutputStream, protocol); + serializeDrainingDataNodes(fileOutputStream, protocol); fileOutputStream.flush(); @@ -319,18 +319,18 @@ public boolean processTakeSnapshot(File snapshotDir) throws IOException, TExcept } } - private void serializeOnlineDataNode(DataOutputStream outputStream, TProtocol protocol) + private void serializeOnlineDataNode(OutputStream outputStream, TProtocol protocol) throws IOException, TException { - outputStream.writeInt(onlineDataNodes.size()); + ReadWriteIOUtils.write(onlineDataNodes.size(), outputStream); for (Entry entry : onlineDataNodes.entrySet()) { - outputStream.writeInt(entry.getKey()); + ReadWriteIOUtils.write(entry.getKey(), outputStream); entry.getValue().write(protocol); } } - private void serializeDrainingDataNodes(DataOutputStream outputStream, TProtocol protocol) + private void serializeDrainingDataNodes(OutputStream outputStream, TProtocol protocol) throws IOException, TException { - outputStream.writeInt(drainingDataNodes.size()); + ReadWriteIOUtils.write(drainingDataNodes.size(), outputStream); for (TDataNodeLocation tDataNodeLocation : drainingDataNodes) { tDataNodeLocation.write(protocol); } @@ -351,17 +351,16 @@ public void processLoadSnapshot(File snapshotDir) throws IOException, TException dataNodeInfoReadWriteLock.writeLock().lock(); try (FileInputStream fileInputStream = new FileInputStream(snapshotFile); - DataInputStream dataInputStream = new DataInputStream(fileInputStream); - TIOStreamTransport tioStreamTransport = new TIOStreamTransport(dataInputStream)) { + TIOStreamTransport tioStreamTransport = new TIOStreamTransport(fileInputStream)) { TProtocol protocol = new TBinaryProtocol(tioStreamTransport); clear(); - nextDataNodeId.set(dataInputStream.readInt()); + nextDataNodeId.set(ReadWriteIOUtils.readInt(fileInputStream)); - deserializeOnlineDataNode(dataInputStream, protocol); + deserializeOnlineDataNode(fileInputStream, protocol); - deserializeDrainingDataNodes(dataInputStream, protocol); + deserializeDrainingDataNodes(fileInputStream, protocol); } finally { configNodeInfoReadWriteLock.writeLock().unlock(); @@ -369,11 +368,11 @@ public void processLoadSnapshot(File snapshotDir) throws IOException, TException } } - private void deserializeOnlineDataNode(DataInputStream inputStream, TProtocol protocol) + private void deserializeOnlineDataNode(InputStream inputStream, TProtocol protocol) throws IOException, TException { - int size = inputStream.readInt(); + int size = ReadWriteIOUtils.readInt(inputStream); while (size > 0) { - int dataNodeId = inputStream.readInt(); + int dataNodeId = ReadWriteIOUtils.readInt(inputStream); TDataNodeInfo dataNodeInfo = new TDataNodeInfo(); dataNodeInfo.read(protocol); onlineDataNodes.put(dataNodeId, dataNodeInfo); @@ -381,9 +380,9 @@ private void deserializeOnlineDataNode(DataInputStream inputStream, TProtocol pr } } - private void deserializeDrainingDataNodes(DataInputStream inputStream, TProtocol protocol) + private void deserializeDrainingDataNodes(InputStream inputStream, TProtocol protocol) throws IOException, TException { - int size = inputStream.readInt(); + int size = ReadWriteIOUtils.readInt(inputStream); while (size > 0) { TDataNodeLocation tDataNodeLocation = new TDataNodeLocation(); tDataNodeLocation.read(protocol); diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/PartitionInfo.java b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/PartitionInfo.java index 851b0d0d519c..2fd2926690cd 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/PartitionInfo.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/PartitionInfo.java @@ -42,6 +42,7 @@ import org.apache.iotdb.consensus.common.DataSet; import org.apache.iotdb.rpc.TSStatusCode; import org.apache.iotdb.tsfile.utils.Pair; +import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils; import org.apache.thrift.TException; import org.apache.thrift.protocol.TBinaryProtocol; @@ -50,12 +51,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.DataInputStream; -import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; @@ -438,18 +439,17 @@ public boolean processTakeSnapshot(File snapshotDir) throws TException, IOExcept lockAllRead(); try (FileOutputStream fileOutputStream = new FileOutputStream(tmpFile); - DataOutputStream dataOutputStream = new DataOutputStream(fileOutputStream); - TIOStreamTransport tioStreamTransport = new TIOStreamTransport(dataOutputStream)) { + TIOStreamTransport tioStreamTransport = new TIOStreamTransport(fileOutputStream)) { TProtocol protocol = new TBinaryProtocol(tioStreamTransport); // serialize nextRegionGroupId - dataOutputStream.writeInt(nextRegionGroupId.get()); + ReadWriteIOUtils.write(nextRegionGroupId.get(), fileOutputStream); // serialize regionMap - serializeRegionMap(dataOutputStream, protocol); + serializeRegionMap(fileOutputStream, protocol); // serialize schemaPartition - schemaPartition.serialize(dataOutputStream, protocol); + schemaPartition.serialize(fileOutputStream, protocol); // serialize dataPartition - dataPartition.serialize(dataOutputStream, protocol); + dataPartition.serialize(fileOutputStream, protocol); // write to file fileOutputStream.flush(); fileOutputStream.close(); @@ -483,16 +483,15 @@ public void processLoadSnapshot(File snapshotDir) throws TException, IOException lockAllWrite(); try (FileInputStream fileInputStream = new FileInputStream(snapshotFile); - DataInputStream dataInputStream = new DataInputStream(fileInputStream); - TIOStreamTransport tioStreamTransport = new TIOStreamTransport(dataInputStream)) { + TIOStreamTransport tioStreamTransport = new TIOStreamTransport(fileInputStream)) { TProtocol protocol = new TBinaryProtocol(tioStreamTransport); // before restoring a snapshot, clear all old data clear(); // start to restore - nextRegionGroupId.set(dataInputStream.readInt()); - deserializeRegionMap(dataInputStream, protocol); - schemaPartition.deserialize(dataInputStream, protocol); - dataPartition.deserialize(dataInputStream, protocol); + nextRegionGroupId.set(ReadWriteIOUtils.readInt(fileInputStream)); + deserializeRegionMap(fileInputStream, protocol); + schemaPartition.deserialize(fileInputStream, protocol); + dataPartition.deserialize(fileInputStream, protocol); } finally { unlockAllWrite(); } @@ -537,9 +536,9 @@ public Map getRegionSlotsCounter() { return regionSlotsCounter; } - private void serializeRegionMap(DataOutputStream dataOutputStream, TProtocol protocol) + private void serializeRegionMap(OutputStream outputStream, TProtocol protocol) throws TException, IOException { - dataOutputStream.writeInt(regionReplicaMap.size()); + ReadWriteIOUtils.write(regionReplicaMap.size(), outputStream); for (TConsensusGroupId consensusGroupId : regionReplicaMap.keySet()) { consensusGroupId.write(protocol); regionReplicaMap.get(consensusGroupId).write(protocol); @@ -547,9 +546,9 @@ private void serializeRegionMap(DataOutputStream dataOutputStream, TProtocol pro } } - private void deserializeRegionMap(DataInputStream dataInputStream, TProtocol protocol) + private void deserializeRegionMap(InputStream inputStream, TProtocol protocol) throws TException, IOException { - int size = dataInputStream.readInt(); + int size = ReadWriteIOUtils.readInt(inputStream); while (size > 0) { TConsensusGroupId tConsensusGroupId = new TConsensusGroupId(); tConsensusGroupId.read(protocol); diff --git a/node-commons/src/main/java/org/apache/iotdb/commons/partition/DataPartition.java b/node-commons/src/main/java/org/apache/iotdb/commons/partition/DataPartition.java index 5ed7fe3e50ea..3fff3e130c0e 100644 --- a/node-commons/src/main/java/org/apache/iotdb/commons/partition/DataPartition.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/partition/DataPartition.java @@ -26,9 +26,9 @@ import org.apache.thrift.TException; import org.apache.thrift.protocol.TProtocol; -import java.io.DataInputStream; -import java.io.DataOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -245,22 +245,22 @@ public void createDataPartition( .put(timePartitionSlot, Collections.singletonList(regionReplicaSet)); } - public void serialize(DataOutputStream dataOutputStream, TProtocol protocol) + public void serialize(OutputStream outputStream, TProtocol protocol) throws IOException, TException { // Map>>> - dataOutputStream.writeInt(dataPartitionMap.size()); + ReadWriteIOUtils.write(dataPartitionMap.size(), outputStream); for (Entry>>> entry : dataPartitionMap.entrySet()) { - ReadWriteIOUtils.write(entry.getKey(), dataOutputStream); - ReadWriteIOUtils.write(entry.getValue().size(), dataOutputStream); + ReadWriteIOUtils.write(entry.getKey(), outputStream); + ReadWriteIOUtils.write(entry.getValue().size(), outputStream); for (Entry>> seriesPartitionSlotMapEntry : entry.getValue().entrySet()) { seriesPartitionSlotMapEntry.getKey().write(protocol); - ReadWriteIOUtils.write(seriesPartitionSlotMapEntry.getValue().size(), dataOutputStream); + ReadWriteIOUtils.write(seriesPartitionSlotMapEntry.getValue().size(), outputStream); for (Entry> timePartitionSlotListEntry : seriesPartitionSlotMapEntry.getValue().entrySet()) { timePartitionSlotListEntry.getKey().write(protocol); - ReadWriteIOUtils.write(timePartitionSlotListEntry.getValue().size(), dataOutputStream); + ReadWriteIOUtils.write(timePartitionSlotListEntry.getValue().size(), outputStream); for (TRegionReplicaSet tRegionReplicaSet : timePartitionSlotListEntry.getValue()) { tRegionReplicaSet.write(protocol); } @@ -269,13 +269,13 @@ public void serialize(DataOutputStream dataOutputStream, TProtocol protocol) } } - public void deserialize(DataInputStream dataInputStream, TProtocol protocol) + public void deserialize(InputStream inputStream, TProtocol protocol) throws TException, IOException { - int storageGroupNum = dataInputStream.readInt(); + int storageGroupNum = ReadWriteIOUtils.readInt(inputStream); // Map>>> while (storageGroupNum > 0) { - String storageGroup = ReadWriteIOUtils.readString(dataInputStream); - int tSeriesPartitionSlotNum = ReadWriteIOUtils.readInt(dataInputStream); + String storageGroup = ReadWriteIOUtils.readString(inputStream); + int tSeriesPartitionSlotNum = ReadWriteIOUtils.readInt(inputStream); Map>> seriesPartitionSlotMapHashMap = new HashMap<>(); @@ -283,14 +283,14 @@ public void deserialize(DataInputStream dataInputStream, TProtocol protocol) TSeriesPartitionSlot tSeriesPartitionSlot = new TSeriesPartitionSlot(); tSeriesPartitionSlot.read(protocol); - int tTimePartitionSlotNum = ReadWriteIOUtils.readInt(dataInputStream); + int tTimePartitionSlotNum = ReadWriteIOUtils.readInt(inputStream); Map> timePartitionSlotListHashMap = new HashMap<>(); while (tTimePartitionSlotNum > 0) { TTimePartitionSlot tTimePartitionSlot = new TTimePartitionSlot(); tTimePartitionSlot.read(protocol); - int size = ReadWriteIOUtils.readInt(dataInputStream); + int size = ReadWriteIOUtils.readInt(inputStream); List tRegionMessageList = new ArrayList<>(); while (size > 0) { diff --git a/node-commons/src/main/java/org/apache/iotdb/commons/partition/SchemaPartition.java b/node-commons/src/main/java/org/apache/iotdb/commons/partition/SchemaPartition.java index 18d1c2a39aa3..e047805edd7a 100644 --- a/node-commons/src/main/java/org/apache/iotdb/commons/partition/SchemaPartition.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/partition/SchemaPartition.java @@ -25,9 +25,9 @@ import org.apache.thrift.TException; import org.apache.thrift.protocol.TProtocol; -import java.io.DataInputStream; -import java.io.DataOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -170,30 +170,30 @@ public void createSchemaPartition( .put(seriesPartitionSlot, regionReplicaSet); } - public void serialize(DataOutputStream dataOutputStream, TProtocol protocol) + public void serialize(OutputStream outputStream, TProtocol protocol) throws IOException, TException { - dataOutputStream.writeInt(schemaPartitionMap.size()); + ReadWriteIOUtils.write(schemaPartitionMap.size(), outputStream); for (Entry> entry : schemaPartitionMap.entrySet()) { - ReadWriteIOUtils.write(entry.getKey(), dataOutputStream); - writeMap(entry.getValue(), dataOutputStream, protocol); + ReadWriteIOUtils.write(entry.getKey(), outputStream); + writeMap(entry.getValue(), outputStream, protocol); } } - public void deserialize(DataInputStream dataInputStream, TProtocol protocol) + public void deserialize(InputStream inputStream, TProtocol protocol) throws TException, IOException { - int size = dataInputStream.readInt(); + int size = ReadWriteIOUtils.readInt(inputStream); while (size > 0) { - String key = ReadWriteIOUtils.readString(dataInputStream); - Map value = readMap(dataInputStream, protocol); + String key = ReadWriteIOUtils.readString(inputStream); + Map value = readMap(inputStream, protocol); schemaPartitionMap.put(key, value); size--; } } private Map readMap( - DataInputStream dataInputStream, TProtocol protocol) throws TException, IOException { - int size = dataInputStream.readInt(); + InputStream inputStream, TProtocol protocol) throws TException, IOException { + int size = ReadWriteIOUtils.readInt(inputStream); Map result = new HashMap<>(); while (size > 0) { TSeriesPartitionSlot tSeriesPartitionSlot = new TSeriesPartitionSlot(); @@ -208,10 +208,10 @@ private Map readMap( private void writeMap( Map valueMap, - DataOutputStream dataOutputStream, + OutputStream outputStream, TProtocol protocol) throws TException, IOException { - dataOutputStream.writeInt(valueMap.size()); + ReadWriteIOUtils.write(valueMap.size(), outputStream); for (Entry entry : valueMap.entrySet()) { entry.getKey().write(protocol); entry.getValue().write(protocol); diff --git a/node-commons/src/test/java/org/apache/iotdb/commons/partition/DataPartitionTest.java b/node-commons/src/test/java/org/apache/iotdb/commons/partition/DataPartitionTest.java index ca9f40d92ef4..9f52323bff4b 100644 --- a/node-commons/src/test/java/org/apache/iotdb/commons/partition/DataPartitionTest.java +++ b/node-commons/src/test/java/org/apache/iotdb/commons/partition/DataPartitionTest.java @@ -32,8 +32,6 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; import java.io.IOException; import java.util.HashMap; import java.util.List; @@ -55,19 +53,17 @@ public void testSerialize() throws TException, IOException { dataPartition.setDataPartitionMap(assignedDataPartition); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream); - TIOStreamTransport tioStreamTransport = new TIOStreamTransport(dataOutputStream); + TIOStreamTransport tioStreamTransport = new TIOStreamTransport(byteArrayOutputStream); TProtocol protocol = new TBinaryProtocol(tioStreamTransport); - dataPartition.serialize(dataOutputStream, protocol); + dataPartition.serialize(byteArrayOutputStream, protocol); DataPartition newDataPartition = new DataPartition(new HashMap<>(), seriesPartitionExecutorClass, 1); ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); - DataInputStream dataInputStream = new DataInputStream(byteArrayInputStream); - tioStreamTransport = new TIOStreamTransport(dataInputStream); + tioStreamTransport = new TIOStreamTransport(byteArrayInputStream); protocol = new TBinaryProtocol(tioStreamTransport); - newDataPartition.deserialize(dataInputStream, protocol); + newDataPartition.deserialize(byteArrayInputStream, protocol); Assert.assertEquals(assignedDataPartition, newDataPartition.getDataPartitionMap()); } } diff --git a/node-commons/src/test/java/org/apache/iotdb/commons/partition/SchemaPartitionTest.java b/node-commons/src/test/java/org/apache/iotdb/commons/partition/SchemaPartitionTest.java index 1d1d7c3f1c56..af8ac595f1af 100644 --- a/node-commons/src/test/java/org/apache/iotdb/commons/partition/SchemaPartitionTest.java +++ b/node-commons/src/test/java/org/apache/iotdb/commons/partition/SchemaPartitionTest.java @@ -31,8 +31,6 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; import java.io.IOException; import java.util.HashMap; import java.util.Map; @@ -51,19 +49,17 @@ public void testSerialize() throws TException, IOException { schemaPartitionFlag, generateTConsensusGroupId(schemaPartitionFlag)); schemaPartition.setSchemaPartitionMap(schemaPartitionMap); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream); - TIOStreamTransport tioStreamTransport = new TIOStreamTransport(dataOutputStream); + TIOStreamTransport tioStreamTransport = new TIOStreamTransport(byteArrayOutputStream); TProtocol protocol = new TBinaryProtocol(tioStreamTransport); - schemaPartition.serialize(dataOutputStream, protocol); + schemaPartition.serialize(byteArrayOutputStream, protocol); SchemaPartition newSchemaPartition = new SchemaPartition(new HashMap<>(), seriesPartitionExecutorClass, 1); ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); - DataInputStream dataInputStream = new DataInputStream(byteArrayInputStream); - tioStreamTransport = new TIOStreamTransport(dataInputStream); + tioStreamTransport = new TIOStreamTransport(byteArrayInputStream); protocol = new TBinaryProtocol(tioStreamTransport); - newSchemaPartition.deserialize(dataInputStream, protocol); + newSchemaPartition.deserialize(byteArrayInputStream, protocol); Assert.assertEquals(schemaPartitionMap, newSchemaPartition.getSchemaPartitionMap()); } } From 5ce066c37b3f6345b7afcbfbb102425d3cb4a884 Mon Sep 17 00:00:00 2001 From: Liao Lanyu <48237151+Plutooooooo@users.noreply.github.com> Date: Thu, 19 May 2022 21:01:57 +0800 Subject: [PATCH 053/436] [IOTDB-3239] fix missing aligned data (#5960) --- .../main/java/org/apache/iotdb/tsfile/read/common/Path.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/Path.java b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/Path.java index 4bd940b30d3a..df6b3920fec0 100644 --- a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/Path.java +++ b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/Path.java @@ -106,7 +106,8 @@ public Path(String device, String measurement) { String[] deviceNodes = PathNodesGenerator.splitPathToNodes(device); this.device = transformNodesToString(deviceNodes, deviceNodes.length); this.measurement = measurement; - this.fullPath = device; + // for aligned path, sensor name for time column is "" + this.fullPath = device + TsFileConstant.PATH_SEPARATOR + measurement; } else if (!StringUtils.isEmpty(measurement)) { String[] measurementNodes = PathNodesGenerator.splitPathToNodes(measurement); this.measurement = transformNodesToString(measurementNodes, measurementNodes.length); From eb4300c9fbbc061d689db6250642541e7d2b665e Mon Sep 17 00:00:00 2001 From: Jackie Tien Date: Thu, 19 May 2022 23:27:58 +0800 Subject: [PATCH 054/436] [IOTDB-3038] Implementation of fill operator (#5837) --- .../operator/process/FillOperator.java | 50 +- .../operator/process/LimitOperator.java | 3 + .../operator/process/LinearFillOperator.java | 191 ++++++++ .../operator/process/OffsetOperator.java | 3 + .../operator/process/fill/IFill.java | 26 ++ .../fill/constant/BinaryConstantFill.java | 63 +++ .../fill/constant/BooleanConstantFill.java | 62 +++ .../fill/constant/DoubleConstantFill.java | 62 +++ .../fill/constant/FloatConstantFill.java | 62 +++ .../fill/constant/IntConstantFill.java | 62 +++ .../fill/constant/LongConstantFill.java | 62 +++ .../process/fill/linear/DoubleLinearFill.java | 94 ++++ .../process/fill/linear/FloatLinearFill.java | 94 ++++ .../process/fill/linear/IntLinearFill.java | 94 ++++ .../process/fill/linear/LinearFill.java | 186 ++++++++ .../process/fill/linear/LongLinearFill.java | 94 ++++ .../fill/previous/BinaryPreviousFill.java | 86 ++++ .../fill/previous/BooleanPreviousFill.java | 85 ++++ .../fill/previous/DoublePreviousFill.java | 85 ++++ .../fill/previous/FloatPreviousFill.java | 85 ++++ .../fill/previous/IntPreviousFill.java | 85 ++++ .../fill/previous/LongPreviousFill.java | 85 ++++ .../plan/planner/LocalExecutionPlanner.java | 150 +++++- .../planner/plan/node/process/FillNode.java | 8 + .../plan/parameter/FillDescriptor.java | 8 + .../statement/literal/BooleanLiteral.java | 9 +- .../plan/statement/literal/DoubleLiteral.java | 14 +- .../mpp/plan/statement/literal/Literal.java | 25 + .../plan/statement/literal/LongLiteral.java | 25 +- .../plan/statement/literal/StringLiteral.java | 6 + .../execution/operator/FillOperatorTest.java | 353 ++++++++++++++ .../operator/LinearFillOperatorTest.java | 441 ++++++++++++++++++ 32 files changed, 2742 insertions(+), 16 deletions(-) create mode 100644 server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/LinearFillOperator.java create mode 100644 server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/IFill.java create mode 100644 server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/constant/BinaryConstantFill.java create mode 100644 server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/constant/BooleanConstantFill.java create mode 100644 server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/constant/DoubleConstantFill.java create mode 100644 server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/constant/FloatConstantFill.java create mode 100644 server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/constant/IntConstantFill.java create mode 100644 server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/constant/LongConstantFill.java create mode 100644 server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/linear/DoubleLinearFill.java create mode 100644 server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/linear/FloatLinearFill.java create mode 100644 server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/linear/IntLinearFill.java create mode 100644 server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/linear/LinearFill.java create mode 100644 server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/linear/LongLinearFill.java create mode 100644 server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/previous/BinaryPreviousFill.java create mode 100644 server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/previous/BooleanPreviousFill.java create mode 100644 server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/previous/DoublePreviousFill.java create mode 100644 server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/previous/FloatPreviousFill.java create mode 100644 server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/previous/IntPreviousFill.java create mode 100644 server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/previous/LongPreviousFill.java create mode 100644 server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/FillOperatorTest.java create mode 100644 server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/LinearFillOperatorTest.java diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/FillOperator.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/FillOperator.java index 02657f0b5605..f853d5d9cd5a 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/FillOperator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/FillOperator.java @@ -18,39 +18,77 @@ */ package org.apache.iotdb.db.mpp.execution.operator.process; +import org.apache.iotdb.db.mpp.execution.operator.Operator; import org.apache.iotdb.db.mpp.execution.operator.OperatorContext; +import org.apache.iotdb.db.mpp.execution.operator.process.fill.IFill; import org.apache.iotdb.tsfile.read.common.block.TsBlock; +import org.apache.iotdb.tsfile.read.common.block.column.Column; import com.google.common.util.concurrent.ListenableFuture; +import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Objects.requireNonNull; + +/** Used for previous and constant value fill */ public class FillOperator implements ProcessOperator { + + private final OperatorContext operatorContext; + private final IFill[] fillArray; + private final Operator child; + private final int outputColumnCount; + + public FillOperator(OperatorContext operatorContext, IFill[] fillArray, Operator child) { + this.operatorContext = requireNonNull(operatorContext, "operatorContext is null"); + checkArgument( + fillArray != null && fillArray.length > 0, "fillArray should not be null or empty"); + this.fillArray = fillArray; + this.child = requireNonNull(child, "child operator is null"); + this.outputColumnCount = fillArray.length; + } + @Override public OperatorContext getOperatorContext() { - return null; + return operatorContext; } @Override public ListenableFuture isBlocked() { - return ProcessOperator.super.isBlocked(); + return child.isBlocked(); } @Override public TsBlock next() { - return null; + TsBlock block = child.next(); + if (block == null) { + return null; + } + + checkArgument( + outputColumnCount == block.getValueColumnCount(), + "outputColumnCount is not equal to value column count of child operator's TsBlock"); + + Column[] valueColumns = new Column[outputColumnCount]; + + for (int i = 0; i < outputColumnCount; i++) { + valueColumns[i] = fillArray[i].fill(block.getColumn(i)); + } + + return TsBlock.wrapBlocksWithoutCopy( + block.getPositionCount(), block.getTimeColumn(), valueColumns); } @Override public boolean hasNext() { - return false; + return child.hasNext(); } @Override public void close() throws Exception { - ProcessOperator.super.close(); + child.close(); } @Override public boolean isFinished() { - return false; + return child.isFinished(); } } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/LimitOperator.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/LimitOperator.java index 9973e256ec17..4ae909a2a8c2 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/LimitOperator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/LimitOperator.java @@ -53,6 +53,9 @@ public ListenableFuture isBlocked() { @Override public TsBlock next() { TsBlock block = child.next(); + if (block == null) { + return null; + } TsBlock res = block; if (block.getPositionCount() <= remainingLimit) { remainingLimit -= block.getPositionCount(); diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/LinearFillOperator.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/LinearFillOperator.java new file mode 100644 index 000000000000..35277da1aef8 --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/LinearFillOperator.java @@ -0,0 +1,191 @@ +/* + * 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.iotdb.db.mpp.execution.operator.process; + +import org.apache.iotdb.db.mpp.execution.operator.Operator; +import org.apache.iotdb.db.mpp.execution.operator.OperatorContext; +import org.apache.iotdb.db.mpp.execution.operator.process.fill.linear.LinearFill; +import org.apache.iotdb.tsfile.read.common.block.TsBlock; +import org.apache.iotdb.tsfile.read.common.block.column.Column; + +import com.google.common.util.concurrent.ListenableFuture; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Objects.requireNonNull; + +/** Used for linear fill */ +public class LinearFillOperator implements ProcessOperator { + + private final OperatorContext operatorContext; + private final LinearFill[] fillArray; + private final Operator child; + private final int outputColumnCount; + // TODO need to spill it to disk if it consumes too much memory + private final List cachedTsBlock; + // next TsBlock Index for each Column + private final int[] nextTsBlockIndex; + + // indicate whether we can call child.next() + // it's used to make sure that child.next() will only be called once in LinearFillOperator.next(); + private boolean canCallNext; + // indicate whether there is more TsBlock for child operator + private boolean noMoreTsBlock; + + public LinearFillOperator( + OperatorContext operatorContext, LinearFill[] fillArray, Operator child) { + this.operatorContext = requireNonNull(operatorContext, "operatorContext is null"); + checkArgument( + fillArray != null && fillArray.length > 0, "fillArray should not be null or empty"); + this.fillArray = fillArray; + this.child = requireNonNull(child, "child operator is null"); + this.outputColumnCount = fillArray.length; + this.cachedTsBlock = new ArrayList<>(); + this.nextTsBlockIndex = new int[outputColumnCount]; + Arrays.fill(this.nextTsBlockIndex, 1); + this.canCallNext = false; + this.noMoreTsBlock = true; + } + + @Override + public OperatorContext getOperatorContext() { + return operatorContext; + } + + @Override + public ListenableFuture isBlocked() { + return child.isBlocked(); + } + + @Override + public TsBlock next() { + + // make sure we call child.next() at most once + if (cachedTsBlock.isEmpty()) { + canCallNext = false; + TsBlock nextTsBlock = child.next(); + // child operator's calculation is not finished, so we just return null + if (nextTsBlock == null || nextTsBlock.isEmpty()) { + return nextTsBlock; + } else { // otherwise, we cache it + cachedTsBlock.add(nextTsBlock); + } + } + + TsBlock originTsBlock = cachedTsBlock.get(0); + long currentEndTime = originTsBlock.getEndTime(); + // Step 1: judge whether we can fill current TsBlock, if TsBlock that we can get is not enough, + // we just return null + for (int columnIndex = 0; columnIndex < outputColumnCount; columnIndex++) { + // current valueColumn can't be filled using current information + if (fillArray[columnIndex].needPrepareForNext( + currentEndTime, originTsBlock.getColumn(columnIndex))) { + // current cached TsBlock is not enough to fill this column + while (!isCachedTsBlockEnough(columnIndex, currentEndTime)) { + // if we failed to get next TsBlock + if (!tryToGetNextTsBlock()) { + // there is no more TsBlock, so we have to fill this Column + if (noMoreTsBlock) { + // break the while-loop, continue to judge next Column + break; + } else { + // there is still more TsBlock, so current calculation is not finished, and we just + // return null + return null; + } + } + } + } + } + // Step 2: fill current TsBlock + originTsBlock = cachedTsBlock.remove(0); + Column[] columns = new Column[outputColumnCount]; + for (int i = 0; i < outputColumnCount; i++) { + columns[i] = fillArray[i].fill(originTsBlock.getTimeColumn(), originTsBlock.getColumn(i)); + } + TsBlock result = + new TsBlock(originTsBlock.getPositionCount(), originTsBlock.getTimeColumn(), columns); + for (int i = 0; i < outputColumnCount; i++) { + nextTsBlockIndex[i]--; + } + return result; + } + + @Override + public boolean hasNext() { + // if child.hasNext() return false, it means that there is no more tsBlocks + noMoreTsBlock = !child.hasNext(); + // if there is more tsBlock, we can call child.next() once + canCallNext = !noMoreTsBlock; + return !cachedTsBlock.isEmpty() || !noMoreTsBlock; + } + + @Override + public void close() throws Exception { + child.close(); + } + + @Override + public boolean isFinished() { + return cachedTsBlock.isEmpty() && child.isFinished(); + } + + /** + * Judge whether we can use current cached TsBlock to fill Column + * + * @param columnIndex index for column which need to be filled + * @param currentEndTime endTime of column which need to be filled + * @return true if current cached TsBlock is enough to fill Column at columnIndex, otherwise + * false. + */ + private boolean isCachedTsBlockEnough(int columnIndex, long currentEndTime) { + // next TsBlock has already been in the cachedTsBlock + while (nextTsBlockIndex[columnIndex] < cachedTsBlock.size()) { + TsBlock nextTsBlock = cachedTsBlock.get(nextTsBlockIndex[columnIndex]); + nextTsBlockIndex[columnIndex]++; + if (fillArray[columnIndex].prepareForNext( + currentEndTime, nextTsBlock.getTimeColumn(), nextTsBlock.getColumn(columnIndex))) { + return true; + } + } + return false; + } + + /** + * @return true if we succeed to get next TsBlock and add it into cachedTsBlock, otherwise false + */ + private boolean tryToGetNextTsBlock() { + if (canCallNext) { // if we can call child.next(), we call that and cache it in + // cachedTsBlock + canCallNext = false; + TsBlock nextTsBlock = child.next(); + // child operator's calculation is not finished, so we just return null + if (nextTsBlock == null || nextTsBlock.isEmpty()) { + return false; + } else { // otherwise, we cache it + cachedTsBlock.add(nextTsBlock); + return true; + } + } + return false; + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/OffsetOperator.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/OffsetOperator.java index 04a7915e5240..b2136a5b4a98 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/OffsetOperator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/OffsetOperator.java @@ -53,6 +53,9 @@ public ListenableFuture isBlocked() { @Override public TsBlock next() { TsBlock block = child.next(); + if (block == null) { + return null; + } if (remainingOffset > 0) { int offset = Math.min((int) remainingOffset, block.getPositionCount()); remainingOffset -= offset; diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/IFill.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/IFill.java new file mode 100644 index 000000000000..d43325a21c4b --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/IFill.java @@ -0,0 +1,26 @@ +/* + * 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.iotdb.db.mpp.execution.operator.process.fill; + +import org.apache.iotdb.tsfile.read.common.block.column.Column; + +public interface IFill { + + Column fill(Column valueColumn); +} diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/constant/BinaryConstantFill.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/constant/BinaryConstantFill.java new file mode 100644 index 000000000000..f590adced394 --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/constant/BinaryConstantFill.java @@ -0,0 +1,63 @@ +/* + * 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.iotdb.db.mpp.execution.operator.process.fill.constant; + +import org.apache.iotdb.db.mpp.execution.operator.process.fill.IFill; +import org.apache.iotdb.tsfile.read.common.block.column.BinaryColumn; +import org.apache.iotdb.tsfile.read.common.block.column.Column; +import org.apache.iotdb.tsfile.read.common.block.column.RunLengthEncodedColumn; +import org.apache.iotdb.tsfile.utils.Binary; + +import java.util.Optional; + +public class BinaryConstantFill implements IFill { + + // fill value + private final Binary value; + // used for constructing RunLengthEncodedColumn, size of it must be 1 + private final Binary[] valueArray; + + public BinaryConstantFill(Binary value) { + this.value = value; + this.valueArray = new Binary[] {value}; + } + + @Override + public Column fill(Column valueColumn) { + int size = valueColumn.getPositionCount(); + // if this valueColumn doesn't have any null value, or it's empty, just return itself; + if (!valueColumn.mayHaveNull() || size == 0) { + return valueColumn; + } + // if its values are all null + if (valueColumn instanceof RunLengthEncodedColumn) { + return new RunLengthEncodedColumn(new BinaryColumn(1, Optional.empty(), valueArray), size); + } else { + Binary[] array = new Binary[size]; + for (int i = 0; i < size; i++) { + if (valueColumn.isNull(i)) { + array[i] = value; + } else { + array[i] = valueColumn.getBinary(i); + } + } + return new BinaryColumn(size, Optional.empty(), array); + } + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/constant/BooleanConstantFill.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/constant/BooleanConstantFill.java new file mode 100644 index 000000000000..7604248242db --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/constant/BooleanConstantFill.java @@ -0,0 +1,62 @@ +/* + * 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.iotdb.db.mpp.execution.operator.process.fill.constant; + +import org.apache.iotdb.db.mpp.execution.operator.process.fill.IFill; +import org.apache.iotdb.tsfile.read.common.block.column.BooleanColumn; +import org.apache.iotdb.tsfile.read.common.block.column.Column; +import org.apache.iotdb.tsfile.read.common.block.column.RunLengthEncodedColumn; + +import java.util.Optional; + +public class BooleanConstantFill implements IFill { + + // fill value + private final boolean value; + // used for constructing RunLengthEncodedColumn + private final boolean[] valueArray; + + public BooleanConstantFill(boolean value) { + this.value = value; + this.valueArray = new boolean[] {value}; + } + + @Override + public Column fill(Column valueColumn) { + int size = valueColumn.getPositionCount(); + // if this valueColumn doesn't have any null value, or it's empty, just return itself; + if (!valueColumn.mayHaveNull() || size == 0) { + return valueColumn; + } + // if its values are all null + if (valueColumn instanceof RunLengthEncodedColumn) { + return new RunLengthEncodedColumn(new BooleanColumn(1, Optional.empty(), valueArray), size); + } else { + boolean[] array = new boolean[size]; + for (int i = 0; i < size; i++) { + if (valueColumn.isNull(i)) { + array[i] = value; + } else { + array[i] = valueColumn.getBoolean(i); + } + } + return new BooleanColumn(size, Optional.empty(), array); + } + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/constant/DoubleConstantFill.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/constant/DoubleConstantFill.java new file mode 100644 index 000000000000..46156193128e --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/constant/DoubleConstantFill.java @@ -0,0 +1,62 @@ +/* + * 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.iotdb.db.mpp.execution.operator.process.fill.constant; + +import org.apache.iotdb.db.mpp.execution.operator.process.fill.IFill; +import org.apache.iotdb.tsfile.read.common.block.column.Column; +import org.apache.iotdb.tsfile.read.common.block.column.DoubleColumn; +import org.apache.iotdb.tsfile.read.common.block.column.RunLengthEncodedColumn; + +import java.util.Optional; + +public class DoubleConstantFill implements IFill { + + // fill value + private final double value; + // used for constructing RunLengthEncodedColumn + private final double[] valueArray; + + public DoubleConstantFill(double value) { + this.value = value; + this.valueArray = new double[] {value}; + } + + @Override + public Column fill(Column valueColumn) { + int size = valueColumn.getPositionCount(); + // if this valueColumn doesn't have any null value, or it's empty, just return itself; + if (!valueColumn.mayHaveNull() || size == 0) { + return valueColumn; + } + // if its values are all null + if (valueColumn instanceof RunLengthEncodedColumn) { + return new RunLengthEncodedColumn(new DoubleColumn(1, Optional.empty(), valueArray), size); + } else { + double[] array = new double[size]; + for (int i = 0; i < size; i++) { + if (valueColumn.isNull(i)) { + array[i] = value; + } else { + array[i] = valueColumn.getDouble(i); + } + } + return new DoubleColumn(size, Optional.empty(), array); + } + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/constant/FloatConstantFill.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/constant/FloatConstantFill.java new file mode 100644 index 000000000000..f7d0dc0f3217 --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/constant/FloatConstantFill.java @@ -0,0 +1,62 @@ +/* + * 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.iotdb.db.mpp.execution.operator.process.fill.constant; + +import org.apache.iotdb.db.mpp.execution.operator.process.fill.IFill; +import org.apache.iotdb.tsfile.read.common.block.column.Column; +import org.apache.iotdb.tsfile.read.common.block.column.FloatColumn; +import org.apache.iotdb.tsfile.read.common.block.column.RunLengthEncodedColumn; + +import java.util.Optional; + +public class FloatConstantFill implements IFill { + + // fill value + private final float value; + // used for constructing RunLengthEncodedColumn + private final float[] valueArray; + + public FloatConstantFill(float value) { + this.value = value; + this.valueArray = new float[] {value}; + } + + @Override + public Column fill(Column valueColumn) { + int size = valueColumn.getPositionCount(); + // if this valueColumn doesn't have any null value, or it's empty, just return itself; + if (!valueColumn.mayHaveNull() || size == 0) { + return valueColumn; + } + // if its values are all null + if (valueColumn instanceof RunLengthEncodedColumn) { + return new RunLengthEncodedColumn(new FloatColumn(1, Optional.empty(), valueArray), size); + } else { + float[] array = new float[size]; + for (int i = 0; i < size; i++) { + if (valueColumn.isNull(i)) { + array[i] = value; + } else { + array[i] = valueColumn.getFloat(i); + } + } + return new FloatColumn(size, Optional.empty(), array); + } + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/constant/IntConstantFill.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/constant/IntConstantFill.java new file mode 100644 index 000000000000..bbe34abf6c89 --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/constant/IntConstantFill.java @@ -0,0 +1,62 @@ +/* + * 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.iotdb.db.mpp.execution.operator.process.fill.constant; + +import org.apache.iotdb.db.mpp.execution.operator.process.fill.IFill; +import org.apache.iotdb.tsfile.read.common.block.column.Column; +import org.apache.iotdb.tsfile.read.common.block.column.IntColumn; +import org.apache.iotdb.tsfile.read.common.block.column.RunLengthEncodedColumn; + +import java.util.Optional; + +public class IntConstantFill implements IFill { + + // fill value + private final int value; + // used for constructing RunLengthEncodedColumn + private final int[] valueArray; + + public IntConstantFill(int value) { + this.value = value; + this.valueArray = new int[] {value}; + } + + @Override + public Column fill(Column valueColumn) { + int size = valueColumn.getPositionCount(); + // if this valueColumn doesn't have any null value, or it's empty, just return itself; + if (!valueColumn.mayHaveNull() || size == 0) { + return valueColumn; + } + // if its values are all null + if (valueColumn instanceof RunLengthEncodedColumn) { + return new RunLengthEncodedColumn(new IntColumn(1, Optional.empty(), valueArray), size); + } else { + int[] array = new int[size]; + for (int i = 0; i < size; i++) { + if (valueColumn.isNull(i)) { + array[i] = value; + } else { + array[i] = valueColumn.getInt(i); + } + } + return new IntColumn(size, Optional.empty(), array); + } + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/constant/LongConstantFill.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/constant/LongConstantFill.java new file mode 100644 index 000000000000..72721980da06 --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/constant/LongConstantFill.java @@ -0,0 +1,62 @@ +/* + * 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.iotdb.db.mpp.execution.operator.process.fill.constant; + +import org.apache.iotdb.db.mpp.execution.operator.process.fill.IFill; +import org.apache.iotdb.tsfile.read.common.block.column.Column; +import org.apache.iotdb.tsfile.read.common.block.column.LongColumn; +import org.apache.iotdb.tsfile.read.common.block.column.RunLengthEncodedColumn; + +import java.util.Optional; + +public class LongConstantFill implements IFill { + + // fill value + private final long value; + // used for constructing RunLengthEncodedColumn + private final long[] valueArray; + + public LongConstantFill(long value) { + this.value = value; + this.valueArray = new long[] {value}; + } + + @Override + public Column fill(Column valueColumn) { + int size = valueColumn.getPositionCount(); + // if this valueColumn doesn't have any null value, or it's empty, just return itself; + if (!valueColumn.mayHaveNull() || size == 0) { + return valueColumn; + } + // if its values are all null + if (valueColumn instanceof RunLengthEncodedColumn) { + return new RunLengthEncodedColumn(new LongColumn(1, Optional.empty(), valueArray), size); + } else { + long[] array = new long[size]; + for (int i = 0; i < size; i++) { + if (valueColumn.isNull(i)) { + array[i] = value; + } else { + array[i] = valueColumn.getLong(i); + } + } + return new LongColumn(size, Optional.empty(), array); + } + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/linear/DoubleLinearFill.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/linear/DoubleLinearFill.java new file mode 100644 index 000000000000..52a228b1e49b --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/linear/DoubleLinearFill.java @@ -0,0 +1,94 @@ +/* + * 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.iotdb.db.mpp.execution.operator.process.fill.linear; + +import org.apache.iotdb.tsfile.read.common.block.column.Column; +import org.apache.iotdb.tsfile.read.common.block.column.DoubleColumn; +import org.apache.iotdb.tsfile.read.common.block.column.DoubleColumnBuilder; + +import java.util.Optional; + +public class DoubleLinearFill extends LinearFill { + + // previous value + private double previousValue; + // next non-null value whose time is closest to the current TsBlock's endTime + private double nextValue; + + private double nextValueInCurrentColumn; + + @Override + void fillValue(Column column, int index, Object array) { + ((double[]) array)[index] = column.getDouble(index); + } + + @Override + void fillValue(Object array, int index) { + ((double[]) array)[index] = getFilledValue(); + } + + @Override + Object createValueArray(int size) { + return new double[size]; + } + + @Override + Column createNullValueColumn() { + return DoubleColumnBuilder.NULL_VALUE_BLOCK; + } + + @Override + Column createFilledValueColumn() { + double filledValue = getFilledValue(); + return new DoubleColumn(1, Optional.empty(), new double[] {filledValue}); + } + + @Override + Column createFilledValueColumn(Object array, boolean[] isNull, boolean hasNullValue, int size) { + if (hasNullValue) { + return new DoubleColumn(size, Optional.of(isNull), (double[]) array); + } else { + return new DoubleColumn(size, Optional.empty(), (double[]) array); + } + } + + @Override + void updatePreviousValue(Column column, int index) { + previousValue = column.getDouble(index); + } + + @Override + void updateNextValue(Column nextValueColumn, int index) { + this.nextValue = nextValueColumn.getDouble(index); + } + + @Override + void updateNextValueInCurrentColumn(Column nextValueColumn, int index) { + this.nextValueInCurrentColumn = nextValueColumn.getDouble(index); + } + + @Override + void updateNextValueInCurrentColumn() { + this.nextValueInCurrentColumn = this.nextValue; + } + + private double getFilledValue() { + return (previousValue + nextValueInCurrentColumn) / 2.0; + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/linear/FloatLinearFill.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/linear/FloatLinearFill.java new file mode 100644 index 000000000000..a32c60d61e2a --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/linear/FloatLinearFill.java @@ -0,0 +1,94 @@ +/* + * 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.iotdb.db.mpp.execution.operator.process.fill.linear; + +import org.apache.iotdb.tsfile.read.common.block.column.Column; +import org.apache.iotdb.tsfile.read.common.block.column.FloatColumn; +import org.apache.iotdb.tsfile.read.common.block.column.FloatColumnBuilder; + +import java.util.Optional; + +public class FloatLinearFill extends LinearFill { + + // previous value + private float previousValue; + // next non-null value whose time is closest to the current TsBlock's endTime + private float nextValue; + + private float nextValueInCurrentColumn; + + @Override + void fillValue(Column column, int index, Object array) { + ((float[]) array)[index] = column.getFloat(index); + } + + @Override + void fillValue(Object array, int index) { + ((float[]) array)[index] = getFilledValue(); + } + + @Override + Object createValueArray(int size) { + return new float[size]; + } + + @Override + Column createNullValueColumn() { + return FloatColumnBuilder.NULL_VALUE_BLOCK; + } + + @Override + Column createFilledValueColumn() { + float filledValue = getFilledValue(); + return new FloatColumn(1, Optional.empty(), new float[] {filledValue}); + } + + @Override + Column createFilledValueColumn(Object array, boolean[] isNull, boolean hasNullValue, int size) { + if (hasNullValue) { + return new FloatColumn(size, Optional.of(isNull), (float[]) array); + } else { + return new FloatColumn(size, Optional.empty(), (float[]) array); + } + } + + @Override + void updatePreviousValue(Column column, int index) { + previousValue = column.getFloat(index); + } + + @Override + void updateNextValue(Column nextValueColumn, int index) { + this.nextValue = nextValueColumn.getFloat(index); + } + + @Override + void updateNextValueInCurrentColumn(Column nextValueColumn, int index) { + this.nextValueInCurrentColumn = nextValueColumn.getFloat(index); + } + + @Override + void updateNextValueInCurrentColumn() { + this.nextValueInCurrentColumn = this.nextValue; + } + + private float getFilledValue() { + return (previousValue + nextValueInCurrentColumn) / 2.0f; + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/linear/IntLinearFill.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/linear/IntLinearFill.java new file mode 100644 index 000000000000..baf48065510e --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/linear/IntLinearFill.java @@ -0,0 +1,94 @@ +/* + * 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.iotdb.db.mpp.execution.operator.process.fill.linear; + +import org.apache.iotdb.tsfile.read.common.block.column.Column; +import org.apache.iotdb.tsfile.read.common.block.column.IntColumn; +import org.apache.iotdb.tsfile.read.common.block.column.IntColumnBuilder; + +import java.util.Optional; + +public class IntLinearFill extends LinearFill { + + // previous value + private int previousValue; + // next non-null value whose time is closest to the current TsBlock's endTime + private int nextValue; + + private int nextValueInCurrentColumn; + + @Override + void fillValue(Column column, int index, Object array) { + ((int[]) array)[index] = column.getInt(index); + } + + @Override + void fillValue(Object array, int index) { + ((int[]) array)[index] = getFilledValue(); + } + + @Override + Object createValueArray(int size) { + return new int[size]; + } + + @Override + Column createNullValueColumn() { + return IntColumnBuilder.NULL_VALUE_BLOCK; + } + + @Override + Column createFilledValueColumn() { + int filledValue = getFilledValue(); + return new IntColumn(1, Optional.empty(), new int[] {filledValue}); + } + + @Override + Column createFilledValueColumn(Object array, boolean[] isNull, boolean hasNullValue, int size) { + if (hasNullValue) { + return new IntColumn(size, Optional.of(isNull), (int[]) array); + } else { + return new IntColumn(size, Optional.empty(), (int[]) array); + } + } + + @Override + void updatePreviousValue(Column column, int index) { + previousValue = column.getInt(index); + } + + @Override + void updateNextValue(Column nextValueColumn, int index) { + this.nextValue = nextValueColumn.getInt(index); + } + + @Override + void updateNextValueInCurrentColumn(Column nextValueColumn, int index) { + this.nextValueInCurrentColumn = nextValueColumn.getInt(index); + } + + @Override + void updateNextValueInCurrentColumn() { + this.nextValueInCurrentColumn = this.nextValue; + } + + private int getFilledValue() { + return (previousValue + nextValueInCurrentColumn) / 2; + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/linear/LinearFill.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/linear/LinearFill.java new file mode 100644 index 000000000000..0e26d2baf560 --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/linear/LinearFill.java @@ -0,0 +1,186 @@ +/* + * 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.iotdb.db.mpp.execution.operator.process.fill.linear; + +import org.apache.iotdb.tsfile.read.common.block.column.Column; +import org.apache.iotdb.tsfile.read.common.block.column.RunLengthEncodedColumn; +import org.apache.iotdb.tsfile.read.common.block.column.TimeColumn; + +import static com.google.common.base.Preconditions.checkArgument; + +/** + * The result of Linear Fill functions at timestamp "T" is calculated by performing a linear fitting + * method on two time series values, one is at the closest timestamp before T, and the other is at + * the closest timestamp after T. Linear Fill function calculation only supports numeric types + * including int, double and float. + */ +public abstract class LinearFill { + + // whether previous value is null + protected boolean previousIsNull = true; + // time of next value + protected long nextTime = Long.MIN_VALUE; + + protected long nextTimeInCurrentColumn; + + /** + * Before we call this method, we need to make sure the nextValue has been prepared or noMoreNext + * has been set to true + * + * @param timeColumn TimeColumn of valueColumn + * @param valueColumn valueColumn that need to be filled + * @return Value Column that has been filled + */ + public Column fill(TimeColumn timeColumn, Column valueColumn) { + int size = valueColumn.getPositionCount(); + // if this valueColumn is empty, just return itself; + if (size == 0) { + return valueColumn; + } + // if this valueColumn doesn't have any null value, record the last value, and then return + // itself. + if (!valueColumn.mayHaveNull()) { + previousIsNull = false; + // update the value using last non-null value + updatePreviousValue(valueColumn, valueColumn.getPositionCount() - 1); + return valueColumn; + } + + // if its values are all null + if (valueColumn instanceof RunLengthEncodedColumn) { + // previous value is null or next value is null, we just return NULL_VALUE_BLOCK + if (previousIsNull || nextTime < timeColumn.getStartTime()) { + return new RunLengthEncodedColumn(createNullValueColumn(), size); + } else { + prepareForNextValueInCurrentColumn( + timeColumn.getEndTime(), timeColumn.getPositionCount() - 1, timeColumn, valueColumn); + return new RunLengthEncodedColumn(createFilledValueColumn(), size); + } + } else { + Object array = createValueArray(size); + boolean[] isNull = new boolean[size]; + // have null value + boolean hasNullValue = false; + + for (int i = 0; i < size; i++) { + // current value is null, we need to fill it + if (valueColumn.isNull(i)) { + long currentTime = timeColumn.getLong(i); + prepareForNextValueInCurrentColumn(currentTime, i + 1, timeColumn, valueColumn); + // we don't fill it, if either previous value or next value is null + if (previousIsNull || nextIsNull(currentTime)) { + isNull[i] = true; + hasNullValue = true; + } else { + // fill value using previous and next value + fillValue(array, i); + } + } else { // current is not null + // fill value using its own value + fillValue(valueColumn, i, array); + // update previous value + updatePreviousValue(valueColumn, i); + previousIsNull = false; + } + } + return createFilledValueColumn(array, isNull, hasNullValue, size); + } + } + + /** + * @param time end time of current valueColumn that need to be filled + * @param valueColumn valueColumn that need to be filled + * @return true if valueColumn can't be filled using current information, and we need to get next + * TsBlock and then call prepareForNext. false if valueColumn can be filled using current + * information, and we can directly call fill() function + */ + public boolean needPrepareForNext(long time, Column valueColumn) { + return time > nextTime && valueColumn.isNull(valueColumn.getPositionCount() - 1); + } + + /** + * @param time end time of current valueColumn that need to be filled + * @param nextTimeColumn TimeColumn of next TsBlock + * @param nextValueColumn Value Column of next TsBlock + * @return true if we get enough information to fill current column, and we can stop getting next + * TsBlock and calling prepareForNext. false if we still don't get enough information to fill + * current column, and still need to keep getting next TsBlock and then call prepareForNext + */ + public boolean prepareForNext(long time, TimeColumn nextTimeColumn, Column nextValueColumn) { + checkArgument( + nextTimeColumn.getPositionCount() > 0 && nextTimeColumn.getLong(0) > time, + "nextColumn's time should be greater than current time"); + if (time <= nextTime) { + return true; + } + + for (int i = 0; i < nextValueColumn.getPositionCount(); i++) { + if (!nextValueColumn.isNull(i)) { + updateNextValue(nextValueColumn, i); + this.nextTime = nextTimeColumn.getLong(i); + return true; + } + } + return false; + } + + private boolean nextIsNull(long time) { + return nextTimeInCurrentColumn <= time; + } + + private void prepareForNextValueInCurrentColumn( + long time, int startIndex, TimeColumn timeColumn, Column valueColumn) { + if (time <= nextTimeInCurrentColumn) { + return; + } + for (int i = startIndex; i < valueColumn.getPositionCount(); i++) { + if (!valueColumn.isNull(i)) { + this.nextTimeInCurrentColumn = timeColumn.getLong(i); + updateNextValueInCurrentColumn(valueColumn, i); + return; + } + } + + // current column's value is not enough for filling, we should use value of next Column + this.nextTimeInCurrentColumn = this.nextTime; + updateNextValueInCurrentColumn(); + } + + abstract void fillValue(Column column, int index, Object array); + + abstract void fillValue(Object array, int index); + + abstract Object createValueArray(int size); + + abstract Column createNullValueColumn(); + + abstract Column createFilledValueColumn(); + + abstract Column createFilledValueColumn( + Object array, boolean[] isNull, boolean hasNullValue, int size); + + abstract void updatePreviousValue(Column column, int index); + + abstract void updateNextValue(Column nextValueColumn, int index); + + abstract void updateNextValueInCurrentColumn(Column nextValueColumn, int index); + + /** update nextValueInCurrentColumn using value of next Column */ + abstract void updateNextValueInCurrentColumn(); +} diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/linear/LongLinearFill.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/linear/LongLinearFill.java new file mode 100644 index 000000000000..04dba1613a36 --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/linear/LongLinearFill.java @@ -0,0 +1,94 @@ +/* + * 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.iotdb.db.mpp.execution.operator.process.fill.linear; + +import org.apache.iotdb.tsfile.read.common.block.column.Column; +import org.apache.iotdb.tsfile.read.common.block.column.LongColumn; +import org.apache.iotdb.tsfile.read.common.block.column.LongColumnBuilder; + +import java.util.Optional; + +public class LongLinearFill extends LinearFill { + + // previous value + private long previousValue; + // next non-null value whose time is closest to the current TsBlock's endTime + private long nextValue; + + private long nextValueInCurrentColumn; + + @Override + void fillValue(Column column, int index, Object array) { + ((long[]) array)[index] = column.getLong(index); + } + + @Override + void fillValue(Object array, int index) { + ((long[]) array)[index] = getFilledValue(); + } + + @Override + Object createValueArray(int size) { + return new long[size]; + } + + @Override + Column createNullValueColumn() { + return LongColumnBuilder.NULL_VALUE_BLOCK; + } + + @Override + Column createFilledValueColumn() { + long filledValue = getFilledValue(); + return new LongColumn(1, Optional.empty(), new long[] {filledValue}); + } + + @Override + Column createFilledValueColumn(Object array, boolean[] isNull, boolean hasNullValue, int size) { + if (hasNullValue) { + return new LongColumn(size, Optional.of(isNull), (long[]) array); + } else { + return new LongColumn(size, Optional.empty(), (long[]) array); + } + } + + @Override + void updatePreviousValue(Column column, int index) { + previousValue = column.getLong(index); + } + + @Override + void updateNextValue(Column nextValueColumn, int index) { + this.nextValue = nextValueColumn.getLong(index); + } + + @Override + void updateNextValueInCurrentColumn(Column nextValueColumn, int index) { + this.nextValueInCurrentColumn = nextValueColumn.getLong(index); + } + + @Override + void updateNextValueInCurrentColumn() { + this.nextValueInCurrentColumn = this.nextValue; + } + + private long getFilledValue() { + return (previousValue + nextValueInCurrentColumn) / 2L; + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/previous/BinaryPreviousFill.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/previous/BinaryPreviousFill.java new file mode 100644 index 000000000000..ef298e54c164 --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/previous/BinaryPreviousFill.java @@ -0,0 +1,86 @@ +/* + * 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.iotdb.db.mpp.execution.operator.process.fill.previous; + +import org.apache.iotdb.db.mpp.execution.operator.process.fill.IFill; +import org.apache.iotdb.tsfile.read.common.block.column.BinaryColumn; +import org.apache.iotdb.tsfile.read.common.block.column.BinaryColumnBuilder; +import org.apache.iotdb.tsfile.read.common.block.column.Column; +import org.apache.iotdb.tsfile.read.common.block.column.RunLengthEncodedColumn; +import org.apache.iotdb.tsfile.utils.Binary; + +import java.util.Optional; + +public class BinaryPreviousFill implements IFill { + + // previous value + private Binary value; + // whether previous value is null + private boolean previousIsNull = true; + + @Override + public Column fill(Column valueColumn) { + int size = valueColumn.getPositionCount(); + // if this valueColumn is empty, just return itself; + if (size == 0) { + return valueColumn; + } + // if this valueColumn doesn't have any null value, record the last value, and then return + // itself. + if (!valueColumn.mayHaveNull()) { + previousIsNull = false; + // update the value using last non-null value + value = valueColumn.getBinary(size - 1); + return valueColumn; + } + // if its values are all null + if (valueColumn instanceof RunLengthEncodedColumn) { + if (previousIsNull) { + return new RunLengthEncodedColumn(BinaryColumnBuilder.NULL_VALUE_BLOCK, size); + } else { + return new RunLengthEncodedColumn( + new BinaryColumn(1, Optional.empty(), new Binary[] {value}), size); + } + } else { + Binary[] array = new Binary[size]; + boolean[] isNull = new boolean[size]; + // have null value + boolean hasNullValue = false; + for (int i = 0; i < size; i++) { + if (valueColumn.isNull(i)) { + if (previousIsNull) { + isNull[i] = true; + hasNullValue = true; + } else { + array[i] = value; + } + } else { + array[i] = valueColumn.getBinary(i); + value = array[i]; + previousIsNull = false; + } + } + if (hasNullValue) { + return new BinaryColumn(size, Optional.of(isNull), array); + } else { + return new BinaryColumn(size, Optional.empty(), array); + } + } + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/previous/BooleanPreviousFill.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/previous/BooleanPreviousFill.java new file mode 100644 index 000000000000..74bf4466a311 --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/previous/BooleanPreviousFill.java @@ -0,0 +1,85 @@ +/* + * 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.iotdb.db.mpp.execution.operator.process.fill.previous; + +import org.apache.iotdb.db.mpp.execution.operator.process.fill.IFill; +import org.apache.iotdb.tsfile.read.common.block.column.BooleanColumn; +import org.apache.iotdb.tsfile.read.common.block.column.BooleanColumnBuilder; +import org.apache.iotdb.tsfile.read.common.block.column.Column; +import org.apache.iotdb.tsfile.read.common.block.column.RunLengthEncodedColumn; + +import java.util.Optional; + +public class BooleanPreviousFill implements IFill { + + // previous value + private boolean value; + // whether previous value is null + private boolean previousIsNull = true; + + @Override + public Column fill(Column valueColumn) { + int size = valueColumn.getPositionCount(); + // if this valueColumn is empty, just return itself; + if (size == 0) { + return valueColumn; + } + // if this valueColumn doesn't have any null value, record the last value, and then return + // itself. + if (!valueColumn.mayHaveNull()) { + previousIsNull = false; + // update the value using last non-null value + value = valueColumn.getBoolean(size - 1); + return valueColumn; + } + // if its values are all null + if (valueColumn instanceof RunLengthEncodedColumn) { + if (previousIsNull) { + return new RunLengthEncodedColumn(BooleanColumnBuilder.NULL_VALUE_BLOCK, size); + } else { + return new RunLengthEncodedColumn( + new BooleanColumn(1, Optional.empty(), new boolean[] {value}), size); + } + } else { + boolean[] array = new boolean[size]; + boolean[] isNull = new boolean[size]; + // have null value + boolean hasNullValue = false; + for (int i = 0; i < size; i++) { + if (valueColumn.isNull(i)) { + if (previousIsNull) { + isNull[i] = true; + hasNullValue = true; + } else { + array[i] = value; + } + } else { + array[i] = valueColumn.getBoolean(i); + value = array[i]; + previousIsNull = false; + } + } + if (hasNullValue) { + return new BooleanColumn(size, Optional.of(isNull), array); + } else { + return new BooleanColumn(size, Optional.empty(), array); + } + } + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/previous/DoublePreviousFill.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/previous/DoublePreviousFill.java new file mode 100644 index 000000000000..044d3987053b --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/previous/DoublePreviousFill.java @@ -0,0 +1,85 @@ +/* + * 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.iotdb.db.mpp.execution.operator.process.fill.previous; + +import org.apache.iotdb.db.mpp.execution.operator.process.fill.IFill; +import org.apache.iotdb.tsfile.read.common.block.column.Column; +import org.apache.iotdb.tsfile.read.common.block.column.DoubleColumn; +import org.apache.iotdb.tsfile.read.common.block.column.DoubleColumnBuilder; +import org.apache.iotdb.tsfile.read.common.block.column.RunLengthEncodedColumn; + +import java.util.Optional; + +public class DoublePreviousFill implements IFill { + + // previous value + private double value; + // whether previous value is null + private boolean previousIsNull = true; + + @Override + public Column fill(Column valueColumn) { + int size = valueColumn.getPositionCount(); + // if this valueColumn is empty, just return itself; + if (size == 0) { + return valueColumn; + } + // if this valueColumn doesn't have any null value, record the last value, and then return + // itself. + if (!valueColumn.mayHaveNull()) { + previousIsNull = false; + // update the value using last non-null value + value = valueColumn.getDouble(size - 1); + return valueColumn; + } + // if its values are all null + if (valueColumn instanceof RunLengthEncodedColumn) { + if (previousIsNull) { + return new RunLengthEncodedColumn(DoubleColumnBuilder.NULL_VALUE_BLOCK, size); + } else { + return new RunLengthEncodedColumn( + new DoubleColumn(1, Optional.empty(), new double[] {value}), size); + } + } else { + double[] array = new double[size]; + boolean[] isNull = new boolean[size]; + // have null value + boolean hasNullValue = false; + for (int i = 0; i < size; i++) { + if (valueColumn.isNull(i)) { + if (previousIsNull) { + isNull[i] = true; + hasNullValue = true; + } else { + array[i] = value; + } + } else { + array[i] = valueColumn.getDouble(i); + value = array[i]; + previousIsNull = false; + } + } + if (hasNullValue) { + return new DoubleColumn(size, Optional.of(isNull), array); + } else { + return new DoubleColumn(size, Optional.empty(), array); + } + } + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/previous/FloatPreviousFill.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/previous/FloatPreviousFill.java new file mode 100644 index 000000000000..6eec0f1eb02b --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/previous/FloatPreviousFill.java @@ -0,0 +1,85 @@ +/* + * 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.iotdb.db.mpp.execution.operator.process.fill.previous; + +import org.apache.iotdb.db.mpp.execution.operator.process.fill.IFill; +import org.apache.iotdb.tsfile.read.common.block.column.Column; +import org.apache.iotdb.tsfile.read.common.block.column.FloatColumn; +import org.apache.iotdb.tsfile.read.common.block.column.FloatColumnBuilder; +import org.apache.iotdb.tsfile.read.common.block.column.RunLengthEncodedColumn; + +import java.util.Optional; + +public class FloatPreviousFill implements IFill { + + // previous value + private float value; + // whether previous value is null + private boolean previousIsNull = true; + + @Override + public Column fill(Column valueColumn) { + int size = valueColumn.getPositionCount(); + // if this valueColumn is empty, just return itself; + if (size == 0) { + return valueColumn; + } + // if this valueColumn doesn't have any null value, record the last value, and then return + // itself. + if (!valueColumn.mayHaveNull()) { + previousIsNull = false; + // update the value using last non-null value + value = valueColumn.getFloat(size - 1); + return valueColumn; + } + // if its values are all null + if (valueColumn instanceof RunLengthEncodedColumn) { + if (previousIsNull) { + return new RunLengthEncodedColumn(FloatColumnBuilder.NULL_VALUE_BLOCK, size); + } else { + return new RunLengthEncodedColumn( + new FloatColumn(1, Optional.empty(), new float[] {value}), size); + } + } else { + float[] array = new float[size]; + boolean[] isNull = new boolean[size]; + // have null value + boolean hasNullValue = false; + for (int i = 0; i < size; i++) { + if (valueColumn.isNull(i)) { + if (previousIsNull) { + isNull[i] = true; + hasNullValue = true; + } else { + array[i] = value; + } + } else { + array[i] = valueColumn.getFloat(i); + value = array[i]; + previousIsNull = false; + } + } + if (hasNullValue) { + return new FloatColumn(size, Optional.of(isNull), array); + } else { + return new FloatColumn(size, Optional.empty(), array); + } + } + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/previous/IntPreviousFill.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/previous/IntPreviousFill.java new file mode 100644 index 000000000000..b2864fad39b7 --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/previous/IntPreviousFill.java @@ -0,0 +1,85 @@ +/* + * 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.iotdb.db.mpp.execution.operator.process.fill.previous; + +import org.apache.iotdb.db.mpp.execution.operator.process.fill.IFill; +import org.apache.iotdb.tsfile.read.common.block.column.Column; +import org.apache.iotdb.tsfile.read.common.block.column.IntColumn; +import org.apache.iotdb.tsfile.read.common.block.column.IntColumnBuilder; +import org.apache.iotdb.tsfile.read.common.block.column.RunLengthEncodedColumn; + +import java.util.Optional; + +public class IntPreviousFill implements IFill { + + // previous value + private int value; + // whether previous value is null + private boolean previousIsNull = true; + + @Override + public Column fill(Column valueColumn) { + int size = valueColumn.getPositionCount(); + // if this valueColumn is empty, just return itself; + if (size == 0) { + return valueColumn; + } + // if this valueColumn doesn't have any null value, record the last value, and then return + // itself. + if (!valueColumn.mayHaveNull()) { + previousIsNull = false; + // update the value using last non-null value + value = valueColumn.getInt(size - 1); + return valueColumn; + } + // if its values are all null + if (valueColumn instanceof RunLengthEncodedColumn) { + if (previousIsNull) { + return new RunLengthEncodedColumn(IntColumnBuilder.NULL_VALUE_BLOCK, size); + } else { + return new RunLengthEncodedColumn( + new IntColumn(1, Optional.empty(), new int[] {value}), size); + } + } else { + int[] array = new int[size]; + boolean[] isNull = new boolean[size]; + // have null value + boolean hasNullValue = false; + for (int i = 0; i < size; i++) { + if (valueColumn.isNull(i)) { + if (previousIsNull) { + isNull[i] = true; + hasNullValue = true; + } else { + array[i] = value; + } + } else { + array[i] = valueColumn.getInt(i); + value = array[i]; + previousIsNull = false; + } + } + if (hasNullValue) { + return new IntColumn(size, Optional.of(isNull), array); + } else { + return new IntColumn(size, Optional.empty(), array); + } + } + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/previous/LongPreviousFill.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/previous/LongPreviousFill.java new file mode 100644 index 000000000000..a90ab9b03964 --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/fill/previous/LongPreviousFill.java @@ -0,0 +1,85 @@ +/* + * 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.iotdb.db.mpp.execution.operator.process.fill.previous; + +import org.apache.iotdb.db.mpp.execution.operator.process.fill.IFill; +import org.apache.iotdb.tsfile.read.common.block.column.Column; +import org.apache.iotdb.tsfile.read.common.block.column.LongColumn; +import org.apache.iotdb.tsfile.read.common.block.column.LongColumnBuilder; +import org.apache.iotdb.tsfile.read.common.block.column.RunLengthEncodedColumn; + +import java.util.Optional; + +public class LongPreviousFill implements IFill { + + // previous value + private long value; + // whether previous value is null + private boolean previousIsNull = true; + + @Override + public Column fill(Column valueColumn) { + int size = valueColumn.getPositionCount(); + // if this valueColumn is empty, just return itself; + if (size == 0) { + return valueColumn; + } + // if this valueColumn doesn't have any null value, record the last value, and then return + // itself. + if (!valueColumn.mayHaveNull()) { + previousIsNull = false; + // update the value using last non-null value + value = valueColumn.getLong(size - 1); + return valueColumn; + } + // if its values are all null + if (valueColumn instanceof RunLengthEncodedColumn) { + if (previousIsNull) { + return new RunLengthEncodedColumn(LongColumnBuilder.NULL_VALUE_BLOCK, size); + } else { + return new RunLengthEncodedColumn( + new LongColumn(1, Optional.empty(), new long[] {value}), size); + } + } else { + long[] array = new long[size]; + boolean[] isNull = new boolean[size]; + // have null value + boolean hasNullValue = false; + for (int i = 0; i < size; i++) { + if (valueColumn.isNull(i)) { + if (previousIsNull) { + isNull[i] = true; + hasNullValue = true; + } else { + array[i] = value; + } + } else { + array[i] = valueColumn.getLong(i); + value = array[i]; + previousIsNull = false; + } + } + if (hasNullValue) { + return new LongColumn(size, Optional.of(isNull), array); + } else { + return new LongColumn(size, Optional.empty(), array); + } + } + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LocalExecutionPlanner.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LocalExecutionPlanner.java index da95d2b075d9..56ab2028057b 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LocalExecutionPlanner.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LocalExecutionPlanner.java @@ -40,12 +40,33 @@ import org.apache.iotdb.db.mpp.execution.operator.process.AggregateOperator; import org.apache.iotdb.db.mpp.execution.operator.process.DeviceMergeOperator; import org.apache.iotdb.db.mpp.execution.operator.process.DeviceViewOperator; +import org.apache.iotdb.db.mpp.execution.operator.process.FillOperator; import org.apache.iotdb.db.mpp.execution.operator.process.FilterOperator; import org.apache.iotdb.db.mpp.execution.operator.process.LimitOperator; +import org.apache.iotdb.db.mpp.execution.operator.process.LinearFillOperator; import org.apache.iotdb.db.mpp.execution.operator.process.OffsetOperator; +import org.apache.iotdb.db.mpp.execution.operator.process.ProcessOperator; import org.apache.iotdb.db.mpp.execution.operator.process.RawDataAggregateOperator; import org.apache.iotdb.db.mpp.execution.operator.process.TimeJoinOperator; import org.apache.iotdb.db.mpp.execution.operator.process.TransformOperator; +import org.apache.iotdb.db.mpp.execution.operator.process.fill.IFill; +import org.apache.iotdb.db.mpp.execution.operator.process.fill.constant.BinaryConstantFill; +import org.apache.iotdb.db.mpp.execution.operator.process.fill.constant.BooleanConstantFill; +import org.apache.iotdb.db.mpp.execution.operator.process.fill.constant.DoubleConstantFill; +import org.apache.iotdb.db.mpp.execution.operator.process.fill.constant.FloatConstantFill; +import org.apache.iotdb.db.mpp.execution.operator.process.fill.constant.IntConstantFill; +import org.apache.iotdb.db.mpp.execution.operator.process.fill.constant.LongConstantFill; +import org.apache.iotdb.db.mpp.execution.operator.process.fill.linear.DoubleLinearFill; +import org.apache.iotdb.db.mpp.execution.operator.process.fill.linear.FloatLinearFill; +import org.apache.iotdb.db.mpp.execution.operator.process.fill.linear.IntLinearFill; +import org.apache.iotdb.db.mpp.execution.operator.process.fill.linear.LinearFill; +import org.apache.iotdb.db.mpp.execution.operator.process.fill.linear.LongLinearFill; +import org.apache.iotdb.db.mpp.execution.operator.process.fill.previous.BinaryPreviousFill; +import org.apache.iotdb.db.mpp.execution.operator.process.fill.previous.BooleanPreviousFill; +import org.apache.iotdb.db.mpp.execution.operator.process.fill.previous.DoublePreviousFill; +import org.apache.iotdb.db.mpp.execution.operator.process.fill.previous.FloatPreviousFill; +import org.apache.iotdb.db.mpp.execution.operator.process.fill.previous.IntPreviousFill; +import org.apache.iotdb.db.mpp.execution.operator.process.fill.previous.LongPreviousFill; import org.apache.iotdb.db.mpp.execution.operator.process.merge.AscTimeComparator; import org.apache.iotdb.db.mpp.execution.operator.process.merge.ColumnMerger; import org.apache.iotdb.db.mpp.execution.operator.process.merge.DescTimeComparator; @@ -98,9 +119,12 @@ import org.apache.iotdb.db.mpp.plan.planner.plan.node.source.SeriesAggregationScanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.source.SeriesScanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.AggregationDescriptor; +import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.FillDescriptor; import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.InputLocation; import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.OutputColumn; +import org.apache.iotdb.db.mpp.plan.statement.component.FillPolicy; import org.apache.iotdb.db.mpp.plan.statement.component.OrderBy; +import org.apache.iotdb.db.mpp.plan.statement.literal.Literal; import org.apache.iotdb.db.utils.datastructure.TimeSelector; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.read.filter.basic.Filter; @@ -449,7 +473,131 @@ public Operator visitDeviceMerge(DeviceMergeNode node, LocalExecutionPlanContext @Override public Operator visitFill(FillNode node, LocalExecutionPlanContext context) { - return super.visitFill(node, context); + Operator child = node.getChild().accept(this, context); + return getFillOperator(node, context, child); + } + + private ProcessOperator getFillOperator( + FillNode node, LocalExecutionPlanContext context, Operator child) { + FillDescriptor descriptor = node.getFillDescriptor(); + List inputDataTypes = getOutputColumnTypes(node.getChild(), context.typeProvider); + int inputColumns = inputDataTypes.size(); + FillPolicy fillPolicy = descriptor.getFillPolicy(); + switch (fillPolicy) { + case VALUE: + Literal literal = descriptor.getFillValue(); + return new FillOperator( + context.instanceContext.addOperatorContext( + context.getNextOperatorId(), + node.getPlanNodeId(), + FillOperator.class.getSimpleName()), + getConstantFill(inputColumns, inputDataTypes, literal), + child); + case PREVIOUS: + return new FillOperator( + context.instanceContext.addOperatorContext( + context.getNextOperatorId(), + node.getPlanNodeId(), + FillOperator.class.getSimpleName()), + getPreviousFill(inputColumns, inputDataTypes), + child); + case LINEAR: + return new LinearFillOperator( + context.instanceContext.addOperatorContext( + context.getNextOperatorId(), + node.getPlanNodeId(), + LinearFillOperator.class.getSimpleName()), + getLinearFill(inputColumns, inputDataTypes), + child); + default: + throw new IllegalArgumentException("Unknown fill policy: " + fillPolicy); + } + } + + private IFill[] getConstantFill( + int inputColumns, List inputDataTypes, Literal literal) { + IFill[] constantFill = new IFill[inputColumns]; + for (int i = 0; i < inputColumns; i++) { + switch (inputDataTypes.get(i)) { + case BOOLEAN: + constantFill[i] = new BooleanConstantFill(literal.getBoolean()); + break; + case TEXT: + constantFill[i] = new BinaryConstantFill(literal.getBinary()); + break; + case INT32: + constantFill[i] = new IntConstantFill(literal.getInt()); + break; + case INT64: + constantFill[i] = new LongConstantFill(literal.getLong()); + break; + case FLOAT: + constantFill[i] = new FloatConstantFill(literal.getFloat()); + break; + case DOUBLE: + constantFill[i] = new DoubleConstantFill(literal.getDouble()); + break; + default: + throw new IllegalArgumentException("Unknown data type: " + inputDataTypes.get(i)); + } + } + return constantFill; + } + + private IFill[] getPreviousFill(int inputColumns, List inputDataTypes) { + IFill[] previousFill = new IFill[inputColumns]; + for (int i = 0; i < inputColumns; i++) { + switch (inputDataTypes.get(i)) { + case BOOLEAN: + previousFill[i] = new BooleanPreviousFill(); + break; + case TEXT: + previousFill[i] = new BinaryPreviousFill(); + break; + case INT32: + previousFill[i] = new IntPreviousFill(); + break; + case INT64: + previousFill[i] = new LongPreviousFill(); + break; + case FLOAT: + previousFill[i] = new FloatPreviousFill(); + break; + case DOUBLE: + previousFill[i] = new DoublePreviousFill(); + break; + default: + throw new IllegalArgumentException("Unknown data type: " + inputDataTypes.get(i)); + } + } + return previousFill; + } + + private LinearFill[] getLinearFill(int inputColumns, List inputDataTypes) { + LinearFill[] linearFill = new LinearFill[inputColumns]; + for (int i = 0; i < inputColumns; i++) { + switch (inputDataTypes.get(i)) { + case INT32: + linearFill[i] = new IntLinearFill(); + break; + case INT64: + linearFill[i] = new LongLinearFill(); + break; + case FLOAT: + linearFill[i] = new FloatLinearFill(); + break; + case DOUBLE: + linearFill[i] = new DoubleLinearFill(); + break; + case BOOLEAN: + case TEXT: + throw new UnsupportedOperationException( + "DataType: " + inputDataTypes.get(i) + " doesn't support linear fill."); + default: + throw new IllegalArgumentException("Unknown data type: " + inputDataTypes.get(i)); + } + } + return linearFill; } @Override diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/process/FillNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/process/FillNode.java index 72b6c1cdf9cc..eaf55ad0d69a 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/process/FillNode.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/process/FillNode.java @@ -57,6 +57,10 @@ public List getChildren() { return ImmutableList.of(child); } + public PlanNode getChild() { + return child; + } + @Override public void addChild(PlanNode child) { this.child = child; @@ -114,4 +118,8 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(super.hashCode(), fillDescriptor, child); } + + public FillDescriptor getFillDescriptor() { + return fillDescriptor; + } } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/parameter/FillDescriptor.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/parameter/FillDescriptor.java index e0ae3faf436d..d695ab5c35fe 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/parameter/FillDescriptor.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/parameter/FillDescriptor.java @@ -60,6 +60,14 @@ public static FillDescriptor deserialize(ByteBuffer byteBuffer) { } } + public FillPolicy getFillPolicy() { + return fillPolicy; + } + + public Literal getFillValue() { + return fillValue; + } + @Override public boolean equals(Object o) { if (this == o) { diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/literal/BooleanLiteral.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/literal/BooleanLiteral.java index dc8657555d41..6c419ce5c9f6 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/literal/BooleanLiteral.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/literal/BooleanLiteral.java @@ -39,10 +39,6 @@ public BooleanLiteral(boolean value) { this.value = value; } - public boolean getValue() { - return value; - } - @Override public void serialize(ByteBuffer byteBuffer) { ReadWriteIOUtils.write(LiteralType.BOOLEAN.ordinal(), byteBuffer); @@ -70,4 +66,9 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(value); } + + @Override + public boolean getBoolean() { + return value; + } } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/literal/DoubleLiteral.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/literal/DoubleLiteral.java index de596dca37d2..d24aed13f93a 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/literal/DoubleLiteral.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/literal/DoubleLiteral.java @@ -36,10 +36,6 @@ public DoubleLiteral(double value) { this.value = value; } - public double getValue() { - return value; - } - @Override public void serialize(ByteBuffer byteBuffer) { ReadWriteIOUtils.write(LiteralType.DOUBLE.ordinal(), byteBuffer); @@ -67,4 +63,14 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(value); } + + @Override + public double getDouble() { + return value; + } + + @Override + public float getFloat() { + return (float) value; + } } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/literal/Literal.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/literal/Literal.java index e35a8cbf2ef1..cd089aa9d737 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/literal/Literal.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/literal/Literal.java @@ -21,6 +21,7 @@ import org.apache.iotdb.db.mpp.plan.statement.StatementNode; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; +import org.apache.iotdb.tsfile.utils.Binary; import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils; import java.nio.ByteBuffer; @@ -56,4 +57,28 @@ public static Literal deserialize(ByteBuffer byteBuffer) { public abstract void serialize(ByteBuffer byteBuffer); public abstract boolean isDataTypeConsistency(TSDataType dataType); + + public boolean getBoolean() { + throw new UnsupportedOperationException(getClass().getName()); + } + + public int getInt() { + throw new UnsupportedOperationException(getClass().getName()); + } + + public long getLong() { + throw new UnsupportedOperationException(getClass().getName()); + } + + public float getFloat() { + throw new UnsupportedOperationException(getClass().getName()); + } + + public double getDouble() { + throw new UnsupportedOperationException(getClass().getName()); + } + + public Binary getBinary() { + throw new UnsupportedOperationException(getClass().getName()); + } } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/literal/LongLiteral.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/literal/LongLiteral.java index 4a177d8ed64d..5b10e106b8e1 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/literal/LongLiteral.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/literal/LongLiteral.java @@ -48,7 +48,30 @@ public void serialize(ByteBuffer byteBuffer) { @Override public boolean isDataTypeConsistency(TSDataType dataType) { - return dataType == TSDataType.INT32 || dataType == TSDataType.INT64; + return dataType == TSDataType.INT32 + || dataType == TSDataType.INT64 + || dataType == TSDataType.FLOAT + || dataType == TSDataType.DOUBLE; + } + + @Override + public int getInt() { + return Math.toIntExact(value); + } + + @Override + public long getLong() { + return value; + } + + @Override + public float getFloat() { + return value; + } + + @Override + public double getDouble() { + return value; } @Override diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/literal/StringLiteral.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/literal/StringLiteral.java index f932e0d094d9..adedd9360308 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/literal/StringLiteral.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/literal/StringLiteral.java @@ -20,6 +20,7 @@ package org.apache.iotdb.db.mpp.plan.statement.literal; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; +import org.apache.iotdb.tsfile.utils.Binary; import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils; import java.nio.ByteBuffer; @@ -47,6 +48,11 @@ public boolean isDataTypeConsistency(TSDataType dataType) { return dataType == TSDataType.TEXT; } + @Override + public Binary getBinary() { + return new Binary(value); + } + @Override public boolean equals(Object o) { if (this == o) { diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/FillOperatorTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/FillOperatorTest.java new file mode 100644 index 000000000000..5cbe7b189fbd --- /dev/null +++ b/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/FillOperatorTest.java @@ -0,0 +1,353 @@ +/* + * 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.iotdb.db.mpp.execution.operator; + +import org.apache.iotdb.commons.concurrent.IoTDBThreadPoolFactory; +import org.apache.iotdb.db.mpp.common.FragmentInstanceId; +import org.apache.iotdb.db.mpp.common.PlanFragmentId; +import org.apache.iotdb.db.mpp.common.QueryId; +import org.apache.iotdb.db.mpp.execution.fragment.FragmentInstanceContext; +import org.apache.iotdb.db.mpp.execution.fragment.FragmentInstanceStateMachine; +import org.apache.iotdb.db.mpp.execution.operator.process.FillOperator; +import org.apache.iotdb.db.mpp.execution.operator.process.fill.IFill; +import org.apache.iotdb.db.mpp.execution.operator.process.fill.constant.DoubleConstantFill; +import org.apache.iotdb.db.mpp.execution.operator.process.fill.previous.IntPreviousFill; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId; +import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; +import org.apache.iotdb.tsfile.read.common.block.TsBlock; +import org.apache.iotdb.tsfile.read.common.block.TsBlockBuilder; + +import org.junit.Test; +import org.testcontainers.shaded.com.google.common.collect.ImmutableList; + +import java.util.concurrent.ExecutorService; + +import static org.apache.iotdb.db.mpp.execution.fragment.FragmentInstanceContext.createFragmentInstanceContext; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class FillOperatorTest { + + @Test + public void batchConstantFillTest() { + ExecutorService instanceNotificationExecutor = + IoTDBThreadPoolFactory.newFixedThreadPool(1, "test-instance-notification"); + try { + QueryId queryId = new QueryId("stub_query"); + FragmentInstanceId instanceId = + new FragmentInstanceId(new PlanFragmentId(queryId, 0), "stub-instance"); + FragmentInstanceStateMachine stateMachine = + new FragmentInstanceStateMachine(instanceId, instanceNotificationExecutor); + FragmentInstanceContext fragmentInstanceContext = + createFragmentInstanceContext(instanceId, stateMachine); + PlanNodeId planNodeId1 = new PlanNodeId("1"); + fragmentInstanceContext.addOperatorContext( + 1, planNodeId1, FillOperator.class.getSimpleName()); + + IFill[] fillArray = + new IFill[] { + new DoubleConstantFill(520.0), + new DoubleConstantFill(520.0), + new DoubleConstantFill(520.0) + }; + FillOperator fillOperator = + new FillOperator( + fragmentInstanceContext.getOperatorContexts().get(0), + fillArray, + new Operator() { + private int index = 0; + + @Override + public OperatorContext getOperatorContext() { + return null; + } + + @Override + public TsBlock next() { + int delta = index * 10000; + TsBlockBuilder builder = + new TsBlockBuilder( + ImmutableList.of( + TSDataType.DOUBLE, TSDataType.DOUBLE, TSDataType.DOUBLE)); + // 1 1.0, null, 100.0 + builder.getTimeColumnBuilder().writeLong(1 + delta); + builder.getColumnBuilder(0).writeDouble(1 + delta); + builder.getColumnBuilder(1).appendNull(); + builder.getColumnBuilder(2).writeDouble(100 + delta); + builder.declarePosition(); + // 2 2.0, 20.0, 200.0 + builder.getTimeColumnBuilder().writeLong(2 + delta); + builder.getColumnBuilder(0).writeDouble(2 + delta); + builder.getColumnBuilder(1).writeDouble(20 + delta); + builder.getColumnBuilder(2).writeDouble(200 + delta); + builder.declarePosition(); + // 3 3.0, 30.0, null + builder.getTimeColumnBuilder().writeLong(3 + delta); + builder.getColumnBuilder(0).writeDouble(3 + delta); + builder.getColumnBuilder(1).writeDouble(30 + delta); + builder.getColumnBuilder(2).appendNull(); + builder.declarePosition(); + // 4 null, 40.0, null + builder.getTimeColumnBuilder().writeLong(4 + delta); + builder.getColumnBuilder(0).appendNull(); + builder.getColumnBuilder(1).writeDouble(40 + delta); + builder.getColumnBuilder(2).appendNull(); + builder.declarePosition(); + // 5 null, null, 500.0 + builder.getTimeColumnBuilder().writeLong(5 + delta); + builder.getColumnBuilder(0).appendNull(); + builder.getColumnBuilder(1).appendNull(); + builder.getColumnBuilder(2).writeDouble(500 + delta); + builder.declarePosition(); + + index++; + return builder.build(); + } + + @Override + public boolean hasNext() { + return index < 3; + } + + @Override + public boolean isFinished() { + return index >= 3; + } + }); + + int count = 0; + double[][][] res = + new double[][][] { + { + {1.0, 520.0, 100.0}, + {2, 20, 200}, + {3, 30, 520.0}, + {520.0, 40, 520.0}, + {520.0, 520.0, 500} + }, + { + {10001, 520.0, 10100}, + {10002, 10020, 10200}, + {10003, 10030, 520.0}, + {520.0, 10040, 520.0}, + {520.0, 520.0, 10500} + }, + { + {20001, 520.0, 20100}, + {20002, 20020, 20200}, + {20003, 20030, 520.0}, + {520.0, 20040, 520.0}, + {520.0, 520.0, 20500} + } + }; + boolean[][][] isNull = + new boolean[][][] { + { + {false, false, false}, + {false, false, false}, + {false, false, false}, + {false, false, false}, + {false, false, false} + }, + { + {false, false, false}, + {false, false, false}, + {false, false, false}, + {false, false, false}, + {false, false, false} + }, + { + {false, false, false}, + {false, false, false}, + {false, false, false}, + {false, false, false}, + {false, false, false} + } + }; + while (fillOperator.hasNext()) { + TsBlock block = fillOperator.next(); + for (int i = 0; i < block.getPositionCount(); i++) { + long expectedTime = i + 1 + count * 10000L; + assertEquals(expectedTime, block.getTimeByIndex(i)); + for (int j = 0; j < 3; j++) { + assertEquals(isNull[count][i][j], block.getColumn(j).isNull(i)); + if (!isNull[count][i][j]) { + assertEquals(res[count][i][j], block.getColumn(j).getDouble(i), 0.00001); + } + } + } + count++; + } + + assertTrue(fillOperator.isFinished()); + assertEquals(3, count); + + } finally { + instanceNotificationExecutor.shutdown(); + } + } + + @Test + public void batchPreviousFillTest() { + ExecutorService instanceNotificationExecutor = + IoTDBThreadPoolFactory.newFixedThreadPool(1, "test-instance-notification"); + try { + QueryId queryId = new QueryId("stub_query"); + FragmentInstanceId instanceId = + new FragmentInstanceId(new PlanFragmentId(queryId, 0), "stub-instance"); + FragmentInstanceStateMachine stateMachine = + new FragmentInstanceStateMachine(instanceId, instanceNotificationExecutor); + FragmentInstanceContext fragmentInstanceContext = + createFragmentInstanceContext(instanceId, stateMachine); + PlanNodeId planNodeId1 = new PlanNodeId("1"); + fragmentInstanceContext.addOperatorContext( + 1, planNodeId1, FillOperator.class.getSimpleName()); + + IFill[] fillArray = + new IFill[] {new IntPreviousFill(), new IntPreviousFill(), new IntPreviousFill()}; + FillOperator fillOperator = + new FillOperator( + fragmentInstanceContext.getOperatorContexts().get(0), + fillArray, + new Operator() { + private int index = 0; + + @Override + public OperatorContext getOperatorContext() { + return null; + } + + @Override + public TsBlock next() { + int delta = index * 10000; + TsBlockBuilder builder = + new TsBlockBuilder( + ImmutableList.of(TSDataType.INT32, TSDataType.INT32, TSDataType.INT32)); + // 1 1, null, 100 + builder.getTimeColumnBuilder().writeLong(1 + delta); + builder.getColumnBuilder(0).writeInt(1 + delta); + builder.getColumnBuilder(1).appendNull(); + builder.getColumnBuilder(2).writeInt(100 + delta); + builder.declarePosition(); + // 2 2, 20, 200 + builder.getTimeColumnBuilder().writeLong(2 + delta); + builder.getColumnBuilder(0).writeInt(2 + delta); + builder.getColumnBuilder(1).writeInt(20 + delta); + builder.getColumnBuilder(2).writeInt(200 + delta); + builder.declarePosition(); + // 3 3, 30, null + builder.getTimeColumnBuilder().writeLong(3 + delta); + builder.getColumnBuilder(0).writeInt(3 + delta); + builder.getColumnBuilder(1).writeInt(30 + delta); + builder.getColumnBuilder(2).appendNull(); + builder.declarePosition(); + // 4 null, 40, null + builder.getTimeColumnBuilder().writeLong(4 + delta); + builder.getColumnBuilder(0).appendNull(); + builder.getColumnBuilder(1).writeInt(40 + delta); + builder.getColumnBuilder(2).appendNull(); + builder.declarePosition(); + // 5 null, null, 500 + builder.getTimeColumnBuilder().writeLong(5 + delta); + builder.getColumnBuilder(0).appendNull(); + builder.getColumnBuilder(1).appendNull(); + builder.getColumnBuilder(2).writeInt(500 + delta); + builder.declarePosition(); + + index++; + return builder.build(); + } + + @Override + public boolean hasNext() { + return index < 3; + } + + @Override + public boolean isFinished() { + return index >= 3; + } + }); + + int count = 0; + int[][][] res = + new int[][][] { + {{1, 0, 100}, {2, 20, 200}, {3, 30, 200}, {3, 40, 200}, {3, 40, 500}}, + { + {10001, 40, 10100}, + {10002, 10020, 10200}, + {10003, 10030, 10200}, + {10003, 10040, 10200}, + {10003, 10040, 10500} + }, + { + {20001, 10040, 20100}, + {20002, 20020, 20200}, + {20003, 20030, 20200}, + {20003, 20040, 20200}, + {20003, 20040, 20500} + } + }; + boolean[][][] isNull = + new boolean[][][] { + { + {false, true, false}, + {false, false, false}, + {false, false, false}, + {false, false, false}, + {false, false, false} + }, + { + {false, false, false}, + {false, false, false}, + {false, false, false}, + {false, false, false}, + {false, false, false} + }, + { + {false, false, false}, + {false, false, false}, + {false, false, false}, + {false, false, false}, + {false, false, false} + } + }; + while (fillOperator.hasNext()) { + TsBlock block = fillOperator.next(); + for (int i = 0; i < block.getPositionCount(); i++) { + long expectedTime = i + 1 + count * 10000L; + assertEquals(expectedTime, block.getTimeByIndex(i)); + for (int j = 0; j < 3; j++) { + assertEquals(isNull[count][i][j], block.getColumn(j).isNull(i)); + if (!isNull[count][i][j]) { + assertEquals(res[count][i][j], block.getColumn(j).getInt(i)); + } + } + } + count++; + } + + assertTrue(fillOperator.isFinished()); + assertEquals(3, count); + + } finally { + instanceNotificationExecutor.shutdown(); + } + } +} diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/LinearFillOperatorTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/LinearFillOperatorTest.java new file mode 100644 index 000000000000..6c17c09d4fd4 --- /dev/null +++ b/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/LinearFillOperatorTest.java @@ -0,0 +1,441 @@ +/* + * 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.iotdb.db.mpp.execution.operator; + +import org.apache.iotdb.commons.concurrent.IoTDBThreadPoolFactory; +import org.apache.iotdb.db.mpp.common.FragmentInstanceId; +import org.apache.iotdb.db.mpp.common.PlanFragmentId; +import org.apache.iotdb.db.mpp.common.QueryId; +import org.apache.iotdb.db.mpp.execution.fragment.FragmentInstanceContext; +import org.apache.iotdb.db.mpp.execution.fragment.FragmentInstanceStateMachine; +import org.apache.iotdb.db.mpp.execution.operator.process.LinearFillOperator; +import org.apache.iotdb.db.mpp.execution.operator.process.fill.linear.FloatLinearFill; +import org.apache.iotdb.db.mpp.execution.operator.process.fill.linear.LinearFill; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId; +import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; +import org.apache.iotdb.tsfile.read.common.block.TsBlock; +import org.apache.iotdb.tsfile.read.common.block.TsBlockBuilder; + +import org.junit.Test; +import org.testcontainers.shaded.com.google.common.collect.ImmutableList; + +import java.util.concurrent.ExecutorService; + +import static org.apache.iotdb.db.mpp.execution.fragment.FragmentInstanceContext.createFragmentInstanceContext; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class LinearFillOperatorTest { + + @Test + public void batchLinearFillTest1() { + ExecutorService instanceNotificationExecutor = + IoTDBThreadPoolFactory.newFixedThreadPool(1, "test-instance-notification"); + try { + QueryId queryId = new QueryId("stub_query"); + FragmentInstanceId instanceId = + new FragmentInstanceId(new PlanFragmentId(queryId, 0), "stub-instance"); + FragmentInstanceStateMachine stateMachine = + new FragmentInstanceStateMachine(instanceId, instanceNotificationExecutor); + FragmentInstanceContext fragmentInstanceContext = + createFragmentInstanceContext(instanceId, stateMachine); + PlanNodeId planNodeId1 = new PlanNodeId("1"); + fragmentInstanceContext.addOperatorContext( + 1, planNodeId1, LinearFillOperator.class.getSimpleName()); + + LinearFill[] fillArray = + new LinearFill[] { + new FloatLinearFill(), + new FloatLinearFill(), + new FloatLinearFill(), + new FloatLinearFill() + }; + LinearFillOperator fillOperator = + new LinearFillOperator( + fragmentInstanceContext.getOperatorContexts().get(0), + fillArray, + new Operator() { + private int index = 0; + private final float[][][] value = + new float[][][] { + { + {1.0f, 0.0f, 3.0f, 4.0f}, + {11.0f, 12.0f, 13.0f, 0.0f}, + {21.0f, 22.0f, 0.0f, 0.0f}, + {0.0f, 32.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 43.0f, 0.0f} + }, + { + {51.0f, 0.0f, 53.0f, 0.0f}, + {61.0f, 62.0f, 63.0f, 0.0f}, + {71.0f, 72.0f, 0.0f, 74.0f}, + {0.0f, 82.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 93.0f, 0.0f} + }, + { + {101.0f, 0.0f, 103.0f, 0.0f}, + {111.0f, 112.0f, 113.0f, 114.0f}, + {121.0f, 122.0f, 0.0f, 124.0f}, + {0.0f, 132.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 143.0f, 0.0f} + } + }; + final boolean[][][] isNull = + new boolean[][][] { + { + {false, true, false, false}, + {false, false, false, true}, + {false, false, true, true}, + {true, false, true, true}, + {true, true, false, true} + }, + { + {false, true, false, true}, + {false, false, false, true}, + {false, false, true, false}, + {true, false, true, true}, + {true, true, false, true} + }, + { + {false, true, false, true}, + {false, false, false, false}, + {false, false, true, false}, + {true, false, true, true}, + {true, true, false, true} + } + }; + + @Override + public OperatorContext getOperatorContext() { + return null; + } + + @Override + public TsBlock next() { + TsBlockBuilder builder = + new TsBlockBuilder( + ImmutableList.of( + TSDataType.FLOAT, + TSDataType.FLOAT, + TSDataType.FLOAT, + TSDataType.FLOAT)); + for (int i = 0; i < 5; i++) { + builder.getTimeColumnBuilder().writeLong(i + index * 5L); + for (int j = 0; j < 4; j++) { + if (isNull[index][i][j]) { + builder.getColumnBuilder(j).appendNull(); + } else { + builder.getColumnBuilder(j).writeFloat(value[index][i][j]); + } + } + builder.declarePosition(); + } + index++; + return builder.build(); + } + + @Override + public boolean hasNext() { + return index < 3; + } + + @Override + public boolean isFinished() { + return index >= 3; + } + }); + + int count = 0; + float[][][] res = + new float[][][] { + { + {1.0f, 0.0f, 3.0f, 4.0f}, + {11.0f, 12.0f, 13.0f, 39.0f}, + {21.0f, 22.0f, 28.0f, 39.0f}, + {36.0f, 32.0f, 28.0f, 39.0f}, + {36.0f, 47.0f, 43.0f, 39.0f} + }, + { + {51.0f, 47.0f, 53.0f, 39.0f}, + {61.0f, 62.0f, 63.0f, 39.0f}, + {71.0f, 72.0f, 78.0f, 74.0f}, + {86.0f, 82.0f, 78.0f, 94.0f}, + {86.0f, 97.0f, 93.0f, 94.0f} + }, + { + {101.0f, 97.0f, 103.0f, 94.0f}, + {111.0f, 112.0f, 113.0f, 114.0f}, + {121.0f, 122.0f, 128.0f, 124.0f}, + {0.0f, 132.0f, 128.0f, 0.0f}, + {0.0f, 0.0f, 143.0f, 0.0f} + } + }; + boolean[][][] isNull = + new boolean[][][] { + { + {false, true, false, false}, + {false, false, false, false}, + {false, false, false, false}, + {false, false, false, false}, + {false, false, false, false} + }, + { + {false, false, false, false}, + {false, false, false, false}, + {false, false, false, false}, + {false, false, false, false}, + {false, false, false, false} + }, + { + {false, false, false, false}, + {false, false, false, false}, + {false, false, false, false}, + {true, false, false, true}, + {true, true, false, true} + } + }; + + boolean[] nullBlock = new boolean[] {true, false, false, false}; + int nullBlockIndex = 0; + while (fillOperator.hasNext()) { + TsBlock block = fillOperator.next(); + assertEquals(nullBlock[nullBlockIndex++], block == null); + if (block == null) { + continue; + } + for (int i = 0; i < block.getPositionCount(); i++) { + long expectedTime = i + count * 5L; + assertEquals(expectedTime, block.getTimeByIndex(i)); + for (int j = 0; j < 4; j++) { + assertEquals(isNull[count][i][j], block.getColumn(j).isNull(i)); + if (!isNull[count][i][j]) { + assertEquals(res[count][i][j], block.getColumn(j).getFloat(i), 0.00001f); + } + } + } + count++; + } + + assertTrue(fillOperator.isFinished()); + assertEquals(3, count); + assertEquals(4, nullBlockIndex); + + } finally { + instanceNotificationExecutor.shutdown(); + } + } + + @Test + public void batchLinearFillTest2() { + ExecutorService instanceNotificationExecutor = + IoTDBThreadPoolFactory.newFixedThreadPool(1, "test-instance-notification"); + try { + QueryId queryId = new QueryId("stub_query"); + FragmentInstanceId instanceId = + new FragmentInstanceId(new PlanFragmentId(queryId, 0), "stub-instance"); + FragmentInstanceStateMachine stateMachine = + new FragmentInstanceStateMachine(instanceId, instanceNotificationExecutor); + FragmentInstanceContext fragmentInstanceContext = + createFragmentInstanceContext(instanceId, stateMachine); + PlanNodeId planNodeId1 = new PlanNodeId("1"); + fragmentInstanceContext.addOperatorContext( + 1, planNodeId1, LinearFillOperator.class.getSimpleName()); + + LinearFill[] fillArray = + new LinearFill[] { + new FloatLinearFill(), + new FloatLinearFill(), + new FloatLinearFill(), + new FloatLinearFill() + }; + LinearFillOperator fillOperator = + new LinearFillOperator( + fragmentInstanceContext.getOperatorContexts().get(0), + fillArray, + new Operator() { + private int index = 0; + private final float[][][] value = + new float[][][] { + { + {1.0f, 0.0f, 3.0f, 4.0f}, + {11.0f, 12.0f, 13.0f, 0.0f}, + {21.0f, 22.0f, 0.0f, 0.0f}, + {0.0f, 32.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 0.0f} + }, + { + {51.0f, 0.0f, 0.0f, 0.0f}, + {61.0f, 62.0f, 0.0f, 0.0f}, + {71.0f, 72.0f, 0.0f, 74.0f}, + {0.0f, 82.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 0.0f} + }, + { + {101.0f, 0.0f, 103.0f, 0.0f}, + {111.0f, 112.0f, 0.0f, 114.0f}, + {121.0f, 122.0f, 0.0f, 124.0f}, + {0.0f, 132.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 0.0f} + } + }; + final boolean[][][] isNull = + new boolean[][][] { + { + {false, true, false, false}, + {false, false, false, true}, + {false, false, true, true}, + {true, false, true, true}, + {true, true, true, true} + }, + { + {false, true, true, true}, + {false, false, true, true}, + {false, false, true, false}, + {true, false, true, true}, + {true, true, true, true} + }, + { + {false, true, false, true}, + {false, false, true, false}, + {false, false, true, false}, + {true, false, true, true}, + {true, true, true, true} + } + }; + + @Override + public OperatorContext getOperatorContext() { + return null; + } + + @Override + public TsBlock next() { + TsBlockBuilder builder = + new TsBlockBuilder( + ImmutableList.of( + TSDataType.FLOAT, + TSDataType.FLOAT, + TSDataType.FLOAT, + TSDataType.FLOAT)); + for (int i = 0; i < 5; i++) { + builder.getTimeColumnBuilder().writeLong(i + index * 5L); + for (int j = 0; j < 4; j++) { + if (isNull[index][i][j]) { + builder.getColumnBuilder(j).appendNull(); + } else { + builder.getColumnBuilder(j).writeFloat(value[index][i][j]); + } + } + builder.declarePosition(); + } + index++; + return builder.build(); + } + + @Override + public boolean hasNext() { + return index < 3; + } + + @Override + public boolean isFinished() { + return index >= 3; + } + }); + + int count = 0; + float[][][] res = + new float[][][] { + { + {1.0f, 0.0f, 3.0f, 4.0f}, + {11.0f, 12.0f, 13.0f, 39.0f}, + {21.0f, 22.0f, 58.0f, 39.0f}, + {36.0f, 32.0f, 58.0f, 39.0f}, + {36.0f, 47.0f, 58.0f, 39.0f} + }, + { + {51.0f, 47.0f, 58.0f, 39.0f}, + {61.0f, 62.0f, 58.0f, 39.0f}, + {71.0f, 72.0f, 58.0f, 74.0f}, + {86.0f, 82.0f, 58.0f, 94.0f}, + {86.0f, 97.0f, 58.0f, 94.0f} + }, + { + {101.0f, 97.0f, 103.0f, 94.0f}, + {111.0f, 112.0f, 0.0f, 114.0f}, + {121.0f, 122.0f, 0.0f, 124.0f}, + {0.0f, 132.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 0.0f} + } + }; + boolean[][][] isNull = + new boolean[][][] { + { + {false, true, false, false}, + {false, false, false, false}, + {false, false, false, false}, + {false, false, false, false}, + {false, false, false, false} + }, + { + {false, false, false, false}, + {false, false, false, false}, + {false, false, false, false}, + {false, false, false, false}, + {false, false, false, false} + }, + { + {false, false, false, false}, + {false, false, true, false}, + {false, false, true, false}, + {true, false, true, true}, + {true, true, true, true} + } + }; + + boolean[] nullBlock = new boolean[] {true, true, false, false, false}; + int nullBlockIndex = 0; + while (fillOperator.hasNext()) { + TsBlock block = fillOperator.next(); + assertEquals(nullBlock[nullBlockIndex++], block == null); + if (block == null) { + continue; + } + for (int i = 0; i < block.getPositionCount(); i++) { + long expectedTime = i + count * 5L; + assertEquals(expectedTime, block.getTimeByIndex(i)); + for (int j = 0; j < 4; j++) { + assertEquals(isNull[count][i][j], block.getColumn(j).isNull(i)); + if (!isNull[count][i][j]) { + assertEquals(res[count][i][j], block.getColumn(j).getFloat(i), 0.00001f); + } + } + } + count++; + } + + assertTrue(fillOperator.isFinished()); + assertEquals(3, count); + assertEquals(5, nullBlockIndex); + + } finally { + instanceNotificationExecutor.shutdown(); + } + } +} From dd4e72e1ba8d8e415087dd950648bab3deb40a41 Mon Sep 17 00:00:00 2001 From: Yifu Zhou Date: Fri, 20 May 2022 11:15:55 +0800 Subject: [PATCH 055/436] [IOTDB-3061] Support Show Child Paths/Nodes in Node Management (#5823) [IOTDB-3061] Support Show Child Paths/Nodes in Node Management (#5823) --- .../consensus/request/ConfigRequest.java | 4 + .../consensus/request/ConfigRequestType.java | 4 +- .../read/GetChildNodesPartitionReq.java | 67 ++++++++++ .../read/GetChildPathsPartitionReq.java | 67 ++++++++++ .../response/SchemaNodeManagementResp.java | 73 +++++++++++ .../confignode/manager/ConfigManager.java | 38 +++++- .../iotdb/confignode/manager/Manager.java | 15 +++ .../confignode/manager/PartitionManager.java | 31 +++++ .../persistence/ClusterSchemaInfo.java | 31 +++++ .../confignode/persistence/PartitionInfo.java | 15 +++ .../executor/ConfigRequestExecutor.java | 43 +++++++ .../thrift/ConfigNodeRPCServiceProcessor.java | 24 ++++ .../ConfigNodeRPCServiceProcessorTest.java | 95 ++++++++++----- .../SchemaNodeManagementPartition.java | 58 +++++++++ .../commons/partition/SchemaPartition.java | 17 +++ .../iotdb/db/client/ConfigNodeClient.java | 19 +++ .../db/mpp/common/header/HeaderConstant.java | 10 ++ .../schema/ChildNodesSchemaScanOperator.java | 93 ++++++++++++++ .../schema/ChildPathsSchemaScanOperator.java | 93 ++++++++++++++ .../schema/NodeManageMemoryMergeOperator.java | 102 ++++++++++++++++ .../iotdb/db/mpp/plan/analyze/Analysis.java | 11 ++ .../iotdb/db/mpp/plan/analyze/Analyzer.java | 54 ++++++++ .../plan/analyze/ClusterPartitionFetcher.java | 44 +++++++ .../analyze/FakePartitionFetcherImpl.java | 8 ++ .../mpp/plan/analyze/IPartitionFetcher.java | 5 + .../analyze/StandalonePartitionFetcher.java | 8 ++ .../memory/StatementMemorySourceVisitor.java | 39 ++++++ .../iotdb/db/mpp/plan/parser/ASTVisitor.java | 22 ++++ .../plan/planner/LocalExecutionPlanner.java | 48 ++++++++ .../mpp/plan/planner/LogicalPlanBuilder.java | 23 ++++ .../db/mpp/plan/planner/LogicalPlanner.java | 25 ++++ .../plan/planner/plan/node/PlanNodeType.java | 14 ++- .../plan/planner/plan/node/PlanVisitor.java | 15 +++ .../read/ChildNodesSchemaScanNode.java | 87 +++++++++++++ .../read/ChildPathsSchemaScanNode.java | 87 +++++++++++++ .../read/NodeManagementMemoryMergeNode.java | 115 ++++++++++++++++++ .../mpp/plan/statement/StatementVisitor.java | 10 ++ .../metadata/ShowChildNodesStatement.java | 41 +++++++ .../metadata/ShowChildPathsStatement.java | 41 +++++++ .../db/mpp/plan/plan/LogicalPlannerTest.java | 39 ++++++ ...odeManagementMemoryMergeNodeSerdeTest.java | 111 +++++++++++++++++ .../src/main/thrift/confignode.thrift | 23 ++++ 42 files changed, 1731 insertions(+), 38 deletions(-) create mode 100644 confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/read/GetChildNodesPartitionReq.java create mode 100644 confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/read/GetChildPathsPartitionReq.java create mode 100644 confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/SchemaNodeManagementResp.java create mode 100644 node-commons/src/main/java/org/apache/iotdb/commons/partition/SchemaNodeManagementPartition.java create mode 100644 server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/schema/ChildNodesSchemaScanOperator.java create mode 100644 server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/schema/ChildPathsSchemaScanOperator.java create mode 100644 server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/schema/NodeManageMemoryMergeOperator.java create mode 100644 server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/metedata/read/ChildNodesSchemaScanNode.java create mode 100644 server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/metedata/read/ChildPathsSchemaScanNode.java create mode 100644 server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/metedata/read/NodeManagementMemoryMergeNode.java create mode 100644 server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/metadata/ShowChildNodesStatement.java create mode 100644 server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/metadata/ShowChildPathsStatement.java create mode 100644 server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/metadata/read/NodeManagementMemoryMergeNodeSerdeTest.java diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigRequest.java b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigRequest.java index dcef6a91f826..26cd531973fe 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigRequest.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigRequest.java @@ -20,6 +20,7 @@ import org.apache.iotdb.confignode.consensus.request.auth.AuthorReq; import org.apache.iotdb.confignode.consensus.request.read.CountStorageGroupReq; +import org.apache.iotdb.confignode.consensus.request.read.GetChildPathsPartitionReq; import org.apache.iotdb.confignode.consensus.request.read.GetDataNodeInfoReq; import org.apache.iotdb.confignode.consensus.request.read.GetDataPartitionReq; import org.apache.iotdb.confignode.consensus.request.read.GetOrCreateDataPartitionReq; @@ -182,6 +183,9 @@ public static ConfigRequest create(ByteBuffer buffer) throws IOException { case ApplyConfigNode: req = new ApplyConfigNodeReq(); break; + case GetChildPathsPartition: + req = new GetChildPathsPartitionReq(); + break; default: throw new IOException("unknown PhysicalPlan type: " + typeNum); } diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigRequestType.java b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigRequestType.java index d0fd464f4f57..78a69f0ffa63 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigRequestType.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigRequestType.java @@ -57,5 +57,7 @@ public enum ConfigRequestType { ListRolePrivilege, ListUserRoles, ListRoleUsers, - ApplyConfigNode + ApplyConfigNode, + GetChildPathsPartition, + GetChildNodesPartition; } diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/read/GetChildNodesPartitionReq.java b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/read/GetChildNodesPartitionReq.java new file mode 100644 index 000000000000..21003b014c50 --- /dev/null +++ b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/read/GetChildNodesPartitionReq.java @@ -0,0 +1,67 @@ +/* + * 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.iotdb.confignode.consensus.request.read; + +import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.confignode.consensus.request.ConfigRequest; +import org.apache.iotdb.confignode.consensus.request.ConfigRequestType; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.Objects; + +public class GetChildNodesPartitionReq extends ConfigRequest { + private PartialPath partialPath; + + public GetChildNodesPartitionReq() { + super(ConfigRequestType.GetChildNodesPartition); + } + + public PartialPath getPartialPath() { + return partialPath; + } + + public void setPartialPath(PartialPath partialPath) { + this.partialPath = partialPath; + } + + @Override + protected void serializeImpl(ByteBuffer buffer) { + partialPath.serialize(buffer); + } + + @Override + protected void deserializeImpl(ByteBuffer buffer) throws IOException { + partialPath = PartialPath.deserialize(buffer); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + GetChildNodesPartitionReq that = (GetChildNodesPartitionReq) o; + return partialPath.equals(that.partialPath); + } + + @Override + public int hashCode() { + return Objects.hash(partialPath); + } +} diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/read/GetChildPathsPartitionReq.java b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/read/GetChildPathsPartitionReq.java new file mode 100644 index 000000000000..4c9274b59449 --- /dev/null +++ b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/read/GetChildPathsPartitionReq.java @@ -0,0 +1,67 @@ +/* + * 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.iotdb.confignode.consensus.request.read; + +import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.confignode.consensus.request.ConfigRequest; +import org.apache.iotdb.confignode.consensus.request.ConfigRequestType; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.Objects; + +public class GetChildPathsPartitionReq extends ConfigRequest { + private PartialPath partialPath; + + public GetChildPathsPartitionReq() { + super(ConfigRequestType.GetChildPathsPartition); + } + + public PartialPath getPartialPath() { + return partialPath; + } + + public void setPartialPath(PartialPath partialPath) { + this.partialPath = partialPath; + } + + @Override + protected void serializeImpl(ByteBuffer buffer) { + partialPath.serialize(buffer); + } + + @Override + protected void deserializeImpl(ByteBuffer buffer) throws IOException { + partialPath = PartialPath.deserialize(buffer); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + GetChildPathsPartitionReq that = (GetChildPathsPartitionReq) o; + return partialPath.equals(that.partialPath); + } + + @Override + public int hashCode() { + return Objects.hash(partialPath); + } +} diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/SchemaNodeManagementResp.java b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/SchemaNodeManagementResp.java new file mode 100644 index 000000000000..d9414ec83270 --- /dev/null +++ b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/SchemaNodeManagementResp.java @@ -0,0 +1,73 @@ +/* + * 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.iotdb.confignode.consensus.response; + +import org.apache.iotdb.common.rpc.thrift.TSStatus; +import org.apache.iotdb.commons.partition.SchemaPartition; +import org.apache.iotdb.confignode.rpc.thrift.TSchemaNodeManagementResp; +import org.apache.iotdb.consensus.common.DataSet; +import org.apache.iotdb.rpc.TSStatusCode; + +import java.util.Set; + +public class SchemaNodeManagementResp implements DataSet { + + private TSStatus status; + + private SchemaPartition schemaPartition; + + private Set matchedNode; + + public SchemaNodeManagementResp() { + // empty constructor + } + + public TSStatus getStatus() { + return status; + } + + public void setStatus(TSStatus status) { + this.status = status; + } + + public void setSchemaPartition(SchemaPartition schemaPartition) { + this.schemaPartition = schemaPartition; + } + + public SchemaPartition getSchemaPartition() { + return schemaPartition; + } + + public Set getMatchedNode() { + return matchedNode; + } + + public void setMatchedNode(Set matchedNode) { + this.matchedNode = matchedNode; + } + + public void convertToRpcSchemaNodeManagementPartitionResp(TSchemaNodeManagementResp resp) { + resp.setStatus(status); + if (status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) { + resp.setSchemaRegionMap(schemaPartition.getSchemaPartitionMap()); + resp.setMatchedNode(matchedNode); + } + } +} diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java index 7324a8a1cb71..31390458d2d7 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java @@ -23,11 +23,14 @@ import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.common.rpc.thrift.TSeriesPartitionSlot; import org.apache.iotdb.commons.conf.CommonDescriptor; +import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.confignode.conf.ConfigNodeConf; import org.apache.iotdb.confignode.conf.ConfigNodeDescriptor; import org.apache.iotdb.confignode.consensus.request.ConfigRequest; import org.apache.iotdb.confignode.consensus.request.auth.AuthorReq; import org.apache.iotdb.confignode.consensus.request.read.CountStorageGroupReq; +import org.apache.iotdb.confignode.consensus.request.read.GetChildNodesPartitionReq; +import org.apache.iotdb.confignode.consensus.request.read.GetChildPathsPartitionReq; import org.apache.iotdb.confignode.consensus.request.read.GetDataNodeInfoReq; import org.apache.iotdb.confignode.consensus.request.read.GetDataPartitionReq; import org.apache.iotdb.confignode.consensus.request.read.GetOrCreateDataPartitionReq; @@ -46,6 +49,7 @@ import org.apache.iotdb.confignode.consensus.response.DataNodeInfosResp; import org.apache.iotdb.confignode.consensus.response.DataPartitionResp; import org.apache.iotdb.confignode.consensus.response.PermissionInfoResp; +import org.apache.iotdb.confignode.consensus.response.SchemaNodeManagementResp; import org.apache.iotdb.confignode.consensus.response.SchemaPartitionResp; import org.apache.iotdb.confignode.consensus.response.StorageGroupSchemaResp; import org.apache.iotdb.confignode.consensus.statemachine.PartitionRegionStateMachine; @@ -303,11 +307,7 @@ public DataSet getSchemaPartition(PathPatternTree patternTree) { partitionSlotsMap = new HashMap<>(); } else { for (String storageGroup : getAllSet) { - if (partitionSlotsMap.containsKey(storageGroup)) { - partitionSlotsMap.replace(storageGroup, new ArrayList<>()); - } else { - partitionSlotsMap.put(storageGroup, new ArrayList<>()); - } + partitionSlotsMap.put(storageGroup, new ArrayList<>()); } } @@ -373,6 +373,34 @@ public DataSet getOrCreateSchemaPartition(PathPatternTree patternTree) { } } + @Override + public DataSet getChildPathsPartition(PartialPath partialPath) { + TSStatus status = confirmLeader(); + if (status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) { + GetChildPathsPartitionReq getChildPathsPartitionReq = new GetChildPathsPartitionReq(); + getChildPathsPartitionReq.setPartialPath(partialPath); + return partitionManager.getChildPathsPartition(getChildPathsPartitionReq); + } else { + SchemaNodeManagementResp dataSet = new SchemaNodeManagementResp(); + dataSet.setStatus(status); + return dataSet; + } + } + + @Override + public DataSet getChildNodesPartition(PartialPath partialPath) { + TSStatus status = confirmLeader(); + if (status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) { + GetChildNodesPartitionReq getChildNodesPartitionReq = new GetChildNodesPartitionReq(); + getChildNodesPartitionReq.setPartialPath(partialPath); + return partitionManager.getChildNodesPartition(getChildNodesPartitionReq); + } else { + SchemaNodeManagementResp dataSet = new SchemaNodeManagementResp(); + dataSet.setStatus(status); + return dataSet; + } + } + @Override public DataSet getDataPartition(GetDataPartitionReq getDataPartitionReq) { TSStatus status = confirmLeader(); diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/Manager.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/Manager.java index dbb95ca560ac..f49654160385 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/Manager.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/Manager.java @@ -19,6 +19,7 @@ package org.apache.iotdb.confignode.manager; import org.apache.iotdb.common.rpc.thrift.TSStatus; +import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.confignode.consensus.request.ConfigRequest; import org.apache.iotdb.confignode.consensus.request.read.CountStorageGroupReq; import org.apache.iotdb.confignode.consensus.request.read.GetDataNodeInfoReq; @@ -153,6 +154,20 @@ public interface Manager { */ DataSet getOrCreateSchemaPartition(PathPatternTree patternTree); + /** + * create SchemaNodeManagementPartition for child paths node management + * + * @return SchemaNodeManagementPartitionDataSet + */ + DataSet getChildPathsPartition(PartialPath partialPath); + + /** + * create SchemaNodeManagementPartition for child nodes node management + * + * @return SchemaNodeManagementPartitionDataSet + */ + DataSet getChildNodesPartition(PartialPath partialPath); + /** * Get DataPartition * diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/PartitionManager.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/PartitionManager.java index 6ce4d6dec9f4..6384604c1536 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/PartitionManager.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/PartitionManager.java @@ -27,6 +27,8 @@ import org.apache.iotdb.commons.partition.executor.SeriesPartitionExecutor; import org.apache.iotdb.confignode.conf.ConfigNodeConf; import org.apache.iotdb.confignode.conf.ConfigNodeDescriptor; +import org.apache.iotdb.confignode.consensus.request.read.GetChildNodesPartitionReq; +import org.apache.iotdb.confignode.consensus.request.read.GetChildPathsPartitionReq; import org.apache.iotdb.confignode.consensus.request.read.GetDataPartitionReq; import org.apache.iotdb.confignode.consensus.request.read.GetOrCreateDataPartitionReq; import org.apache.iotdb.confignode.consensus.request.read.GetOrCreateSchemaPartitionReq; @@ -34,6 +36,7 @@ import org.apache.iotdb.confignode.consensus.request.write.CreateDataPartitionReq; import org.apache.iotdb.confignode.consensus.request.write.CreateSchemaPartitionReq; import org.apache.iotdb.confignode.consensus.response.DataPartitionResp; +import org.apache.iotdb.confignode.consensus.response.SchemaNodeManagementResp; import org.apache.iotdb.confignode.consensus.response.SchemaPartitionResp; import org.apache.iotdb.confignode.exception.NotEnoughDataNodeException; import org.apache.iotdb.confignode.manager.load.LoadManager; @@ -334,6 +337,34 @@ public List getRegionReplicaSets(List grou return partitionInfo.getRegionReplicaSets(groupIds); } + /** + * Get ChildPathsPartition + * + * @param physicalPlan GetChildNodesPartitionReq + * @return SchemaNodeManagementPartitionDataSet that contains only existing matched + * SchemaPartition and matched child paths aboveMtree + */ + public DataSet getChildPathsPartition(GetChildPathsPartitionReq physicalPlan) { + SchemaNodeManagementResp schemaNodeManagementResp; + ConsensusReadResponse consensusReadResponse = getConsensusManager().read(physicalPlan); + schemaNodeManagementResp = (SchemaNodeManagementResp) consensusReadResponse.getDataset(); + return schemaNodeManagementResp; + } + + /** + * Get ChildNodesPartition + * + * @param physicalPlan GetChildNodesPartitionReq + * @return SchemaNodeManagementPartitionDataSet that contains only existing matched + * SchemaPartition and matched child nodes aboveMtree + */ + public DataSet getChildNodesPartition(GetChildNodesPartitionReq physicalPlan) { + SchemaNodeManagementResp schemaNodeManagementResp; + ConsensusReadResponse consensusReadResponse = getConsensusManager().read(physicalPlan); + schemaNodeManagementResp = (SchemaNodeManagementResp) consensusReadResponse.getDataset(); + return schemaNodeManagementResp; + } + private ConsensusManager getConsensusManager() { return configManager.getConsensusManager(); } diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/ClusterSchemaInfo.java b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/ClusterSchemaInfo.java index 12e56b46ee13..53efa7453c24 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/ClusterSchemaInfo.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/ClusterSchemaInfo.java @@ -42,6 +42,7 @@ import org.apache.iotdb.db.metadata.mnode.StorageGroupMNode; import org.apache.iotdb.db.metadata.mtree.MTreeAboveSG; import org.apache.iotdb.rpc.TSStatusCode; +import org.apache.iotdb.tsfile.utils.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -54,8 +55,10 @@ import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.UUID; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -494,6 +497,34 @@ public void processLoadSnapshot(File snapshotDir) throws IOException { } } + public Pair, Set> getChildNodePathInNextLevel(PartialPath partialPath) { + Pair, Set> matchedPathsInNextLevel = + new Pair(new HashSet<>(), new HashSet<>()); + storageGroupReadWriteLock.readLock().lock(); + try { + matchedPathsInNextLevel = mTree.getChildNodePathInNextLevel(partialPath); + } catch (MetadataException e) { + LOGGER.error("Error get matched paths in next level.", e); + } finally { + storageGroupReadWriteLock.readLock().unlock(); + } + return matchedPathsInNextLevel; + } + + public Pair, Set> getChildNodeNameInNextLevel(PartialPath partialPath) { + Pair, Set> matchedNamesInNextLevel = + new Pair(new HashSet<>(), new HashSet<>()); + storageGroupReadWriteLock.readLock().lock(); + try { + matchedNamesInNextLevel = mTree.getChildNodeNameInNextLevel(partialPath); + } catch (MetadataException e) { + LOGGER.error("Error get matched names in next level.", e); + } finally { + storageGroupReadWriteLock.readLock().unlock(); + } + return matchedNamesInNextLevel; + } + @TestOnly public void clear() { mTree.clear(); diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/PartitionInfo.java b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/PartitionInfo.java index 2fd2926690cd..4425fa9d22f2 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/PartitionInfo.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/PartitionInfo.java @@ -37,6 +37,7 @@ import org.apache.iotdb.confignode.consensus.request.write.DeleteRegionsReq; import org.apache.iotdb.confignode.consensus.request.write.DeleteStorageGroupReq; import org.apache.iotdb.confignode.consensus.response.DataPartitionResp; +import org.apache.iotdb.confignode.consensus.response.SchemaNodeManagementResp; import org.apache.iotdb.confignode.consensus.response.SchemaPartitionResp; import org.apache.iotdb.confignode.rpc.thrift.TStorageGroupSchema; import org.apache.iotdb.consensus.common.DataSet; @@ -497,6 +498,20 @@ public void processLoadSnapshot(File snapshotDir) throws TException, IOException } } + /** Get SchemaNodeManagementPartition */ + public DataSet getSchemaNodeManagementPartition(List matchedStroageGroups) { + SchemaNodeManagementResp schemaNodeManagementResp = new SchemaNodeManagementResp(); + schemaPartitionReadWriteLock.readLock().lock(); + try { + schemaNodeManagementResp.setSchemaPartition( + schemaPartition.getSchemaPartition(matchedStroageGroups)); + } finally { + schemaPartitionReadWriteLock.readLock().unlock(); + schemaNodeManagementResp.setStatus(new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode())); + } + return schemaNodeManagementResp; + } + private void lockAllWrite() { regionReadWriteLock.writeLock().lock(); schemaPartitionReadWriteLock.writeLock().lock(); diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigRequestExecutor.java b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigRequestExecutor.java index 4b7bc5d7ced6..8f363bbb63db 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigRequestExecutor.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigRequestExecutor.java @@ -20,10 +20,14 @@ import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.commons.auth.AuthException; +import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.commons.snapshot.SnapshotProcessor; import org.apache.iotdb.confignode.consensus.request.ConfigRequest; +import org.apache.iotdb.confignode.consensus.request.ConfigRequestType; import org.apache.iotdb.confignode.consensus.request.auth.AuthorReq; import org.apache.iotdb.confignode.consensus.request.read.CountStorageGroupReq; +import org.apache.iotdb.confignode.consensus.request.read.GetChildNodesPartitionReq; +import org.apache.iotdb.confignode.consensus.request.read.GetChildPathsPartitionReq; import org.apache.iotdb.confignode.consensus.request.read.GetDataNodeInfoReq; import org.apache.iotdb.confignode.consensus.request.read.GetDataPartitionReq; import org.apache.iotdb.confignode.consensus.request.read.GetSchemaPartitionReq; @@ -42,6 +46,7 @@ import org.apache.iotdb.confignode.consensus.request.write.SetTTLReq; import org.apache.iotdb.confignode.consensus.request.write.SetTimePartitionIntervalReq; import org.apache.iotdb.confignode.consensus.request.write.UpdateProcedureReq; +import org.apache.iotdb.confignode.consensus.response.SchemaNodeManagementResp; import org.apache.iotdb.confignode.exception.physical.UnknownPhysicalPlanTypeException; import org.apache.iotdb.confignode.persistence.AuthorInfo; import org.apache.iotdb.confignode.persistence.ClusterSchemaInfo; @@ -50,6 +55,7 @@ import org.apache.iotdb.confignode.persistence.ProcedureInfo; import org.apache.iotdb.consensus.common.DataSet; import org.apache.iotdb.rpc.TSStatusCode; +import org.apache.iotdb.tsfile.utils.Pair; import org.apache.thrift.TException; import org.slf4j.Logger; @@ -59,6 +65,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; public class ConfigRequestExecutor { @@ -115,6 +122,9 @@ public DataSet executorQueryPlan(ConfigRequest req) return authorInfo.executeListUserRoles((AuthorReq) req); case ListRoleUsers: return authorInfo.executeListRoleUsers((AuthorReq) req); + case GetChildPathsPartition: + case GetChildNodesPartition: + return getSchemaNodeManagementPartiton(req); default: throw new UnknownPhysicalPlanTypeException(req.getType()); } @@ -239,6 +249,39 @@ public void loadSnapshot(File latestSnapshotRootDir) { }); } + private DataSet getSchemaNodeManagementPartiton(ConfigRequest req) + throws UnknownPhysicalPlanTypeException { + Pair, Set> matchedChildInNextLevel; + List matchedStorageGroups = new ArrayList<>(); + if (req.getType() == ConfigRequestType.GetChildPathsPartition) { + GetChildPathsPartitionReq getChildPathsPartitionReq = (GetChildPathsPartitionReq) req; + + // Pair.left means already find matched child paths from aboveMtree, + // Pair.right means need more info from DataNode's schemaRegion. + matchedChildInNextLevel = + clusterSchemaInfo.getChildNodePathInNextLevel(getChildPathsPartitionReq.getPartialPath()); + } else if (req.getType() == ConfigRequestType.GetChildNodesPartition) { + GetChildNodesPartitionReq getChildNodesPartitionReq = (GetChildNodesPartitionReq) req; + + // Pair.left means already find matched child paths from aboveMtree, + // Pair.right means need more info from DataNode's schemaRegion. + matchedChildInNextLevel = + clusterSchemaInfo.getChildNodeNameInNextLevel(getChildNodesPartitionReq.getPartialPath()); + } else { + throw new UnknownPhysicalPlanTypeException(req.getType()); + } + matchedChildInNextLevel.right.forEach( + childPath -> matchedStorageGroups.add(childPath.getFullPath())); + SchemaNodeManagementResp schemaNodeManagementResp = + (SchemaNodeManagementResp) + partitionInfo.getSchemaNodeManagementPartition(matchedStorageGroups); + if (schemaNodeManagementResp.getStatus().getCode() + == TSStatusCode.SUCCESS_STATUS.getStatusCode()) { + schemaNodeManagementResp.setMatchedNode(matchedChildInNextLevel.left); + } + return schemaNodeManagementResp; + } + private List getAllAttributes() { List allAttributes = new ArrayList<>(); allAttributes.add(clusterSchemaInfo); diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessor.java b/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessor.java index c9ec115ad28b..3b20df5131c8 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessor.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessor.java @@ -22,6 +22,7 @@ import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.commons.auth.AuthException; import org.apache.iotdb.commons.conf.CommonDescriptor; +import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.commons.utils.TestOnly; import org.apache.iotdb.confignode.conf.ConfigNodeDescriptor; import org.apache.iotdb.confignode.consensus.request.ConfigRequestType; @@ -43,11 +44,13 @@ import org.apache.iotdb.confignode.consensus.response.DataNodeInfosResp; import org.apache.iotdb.confignode.consensus.response.DataPartitionResp; import org.apache.iotdb.confignode.consensus.response.PermissionInfoResp; +import org.apache.iotdb.confignode.consensus.response.SchemaNodeManagementResp; import org.apache.iotdb.confignode.consensus.response.SchemaPartitionResp; import org.apache.iotdb.confignode.consensus.response.StorageGroupSchemaResp; import org.apache.iotdb.confignode.manager.ConfigManager; import org.apache.iotdb.confignode.manager.ConsensusManager; import org.apache.iotdb.confignode.rpc.thrift.ConfigIService; +import org.apache.iotdb.confignode.rpc.thrift.NodeManagementType; import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerReq; import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerResp; import org.apache.iotdb.confignode.rpc.thrift.TCheckUserPrivilegesReq; @@ -62,6 +65,8 @@ import org.apache.iotdb.confignode.rpc.thrift.TDeleteStorageGroupReq; import org.apache.iotdb.confignode.rpc.thrift.TDeleteStorageGroupsReq; import org.apache.iotdb.confignode.rpc.thrift.TLoginReq; +import org.apache.iotdb.confignode.rpc.thrift.TSchemaNodeManagementReq; +import org.apache.iotdb.confignode.rpc.thrift.TSchemaNodeManagementResp; import org.apache.iotdb.confignode.rpc.thrift.TSchemaPartitionReq; import org.apache.iotdb.confignode.rpc.thrift.TSchemaPartitionResp; import org.apache.iotdb.confignode.rpc.thrift.TSetDataReplicationFactorReq; @@ -258,6 +263,25 @@ public TSchemaPartitionResp getOrCreateSchemaPartition(TSchemaPartitionReq req) return resp; } + @Override + public TSchemaNodeManagementResp getSchemaNodeManagementPartition(TSchemaNodeManagementReq req) + throws TException { + PathPatternTree patternTree = + PathPatternTree.deserialize(ByteBuffer.wrap(req.getPathPatternTree())); + PartialPath partialPath = patternTree.splitToPathList().get(0); + SchemaNodeManagementResp schemaNodeManagementResp; + if (req.getType() == NodeManagementType.CHILD_PATHS) { + schemaNodeManagementResp = + (SchemaNodeManagementResp) configManager.getChildPathsPartition(partialPath); + } else { + schemaNodeManagementResp = + (SchemaNodeManagementResp) configManager.getChildNodesPartition(partialPath); + } + TSchemaNodeManagementResp resp = new TSchemaNodeManagementResp(); + schemaNodeManagementResp.convertToRpcSchemaNodeManagementPartitionResp(resp); + return resp; + } + @Override public TDataPartitionResp getDataPartition(TDataPartitionReq req) throws TException { GetDataPartitionReq getDataPartitionReq = new GetDataPartitionReq(); diff --git a/confignode/src/test/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessorTest.java b/confignode/src/test/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessorTest.java index 278c15b6985e..a3d02b596dfb 100644 --- a/confignode/src/test/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessorTest.java +++ b/confignode/src/test/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessorTest.java @@ -38,6 +38,7 @@ import org.apache.iotdb.confignode.conf.ConfigNodeStartupCheck; import org.apache.iotdb.confignode.manager.ConfigManager; import org.apache.iotdb.confignode.procedure.impl.DeleteStorageGroupProcedure; +import org.apache.iotdb.confignode.rpc.thrift.NodeManagementType; import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerReq; import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerResp; import org.apache.iotdb.confignode.rpc.thrift.TCheckUserPrivilegesReq; @@ -49,6 +50,8 @@ import org.apache.iotdb.confignode.rpc.thrift.TDataPartitionResp; import org.apache.iotdb.confignode.rpc.thrift.TDeleteStorageGroupsReq; import org.apache.iotdb.confignode.rpc.thrift.TGlobalConfig; +import org.apache.iotdb.confignode.rpc.thrift.TSchemaNodeManagementReq; +import org.apache.iotdb.confignode.rpc.thrift.TSchemaNodeManagementResp; import org.apache.iotdb.confignode.rpc.thrift.TSchemaPartitionReq; import org.apache.iotdb.confignode.rpc.thrift.TSchemaPartitionResp; import org.apache.iotdb.confignode.rpc.thrift.TSetDataReplicationFactorReq; @@ -147,7 +150,7 @@ private void registerDataNodes() throws TException { } @Test - public void registerAndQueryDataNodeTest() throws TException { + public void testRegisterAndQueryDataNode() throws TException { registerDataNodes(); // test success re-register @@ -201,7 +204,7 @@ public void registerAndQueryDataNodeTest() throws TException { } @Test - public void setAndQueryStorageGroupTest() throws TException { + public void testSetAndQueryStorageGroup() throws TException { TSStatus status; final String sg0 = "root.sg0"; final String sg1 = "root.sg1"; @@ -303,7 +306,7 @@ private ByteBuffer generatePatternTreeBuffer(String[] paths) } @Test - public void getAndCreateSchemaPartitionTest() + public void testGetAndCreateSchemaPartition() throws TException, IOException, IllegalPathException { final String sg = "root.sg"; final String sg0 = "root.sg0"; @@ -499,7 +502,7 @@ private void checkDataPartitionMap( } @Test - public void getAndCreateDataPartitionTest() throws TException { + public void testGetAndCreateDataPartition() throws TException { final String sg = "root.sg"; final int storageGroupNum = 2; final int seriesPartitionSlotNum = 4; @@ -559,7 +562,7 @@ public void getAndCreateDataPartitionTest() throws TException { } @Test - public void permissionTest() throws TException { + public void testPermission() throws TException { TSStatus status; List userList = new ArrayList<>(); @@ -895,32 +898,6 @@ public void permissionTest() throws TException { } } - @Test - public void deleteStorageGroupTest() throws TException { - TSStatus status; - final String sg0 = "root.sg0"; - final String sg1 = "root.sg1"; - // register DataNodes - registerDataNodes(); - TSetStorageGroupReq setReq0 = new TSetStorageGroupReq(new TStorageGroupSchema(sg0)); - // set StorageGroup0 by default values - status = processor.setStorageGroup(setReq0); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); - // set StorageGroup1 by specific values - TSetStorageGroupReq setReq1 = new TSetStorageGroupReq(new TStorageGroupSchema(sg1)); - status = processor.setStorageGroup(setReq1); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); - TDeleteStorageGroupsReq deleteStorageGroupsReq = new TDeleteStorageGroupsReq(); - List sgs = Arrays.asList(sg0, sg1); - deleteStorageGroupsReq.setPrefixPathList(sgs); - DeleteStorageGroupProcedure.setByPassForTest(true); - TSStatus deleteSgStatus = processor.deleteStorageGroups(deleteStorageGroupsReq); - TStorageGroupSchemaResp root = - processor.getMatchedStorageGroupSchemas(Arrays.asList("root", "*")); - Assert.assertTrue(root.getStorageGroupSchemaMap().isEmpty()); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), deleteSgStatus.getCode()); - } - private void cleanUserAndRole() throws TException { TSStatus status; @@ -972,4 +949,60 @@ private void cleanUserAndRole() throws TException { Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); } } + + @Test + public void testDeleteStorageGroup() throws TException { + TSStatus status; + final String sg0 = "root.sg0"; + final String sg1 = "root.sg1"; + // register DataNodes + registerDataNodes(); + TSetStorageGroupReq setReq0 = new TSetStorageGroupReq(new TStorageGroupSchema(sg0)); + // set StorageGroup0 by default values + status = processor.setStorageGroup(setReq0); + Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + // set StorageGroup1 by specific values + TSetStorageGroupReq setReq1 = new TSetStorageGroupReq(new TStorageGroupSchema(sg1)); + status = processor.setStorageGroup(setReq1); + Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + TDeleteStorageGroupsReq deleteStorageGroupsReq = new TDeleteStorageGroupsReq(); + List sgs = Arrays.asList(sg0, sg1); + deleteStorageGroupsReq.setPrefixPathList(sgs); + DeleteStorageGroupProcedure.setByPassForTest(true); + TSStatus deleteSgStatus = processor.deleteStorageGroups(deleteStorageGroupsReq); + TStorageGroupSchemaResp root = + processor.getMatchedStorageGroupSchemas(Arrays.asList("root", "*")); + Assert.assertTrue(root.getStorageGroupSchemaMap().isEmpty()); + Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), deleteSgStatus.getCode()); + } + + @Test + public void testGetSchemaNodeManagementPartition() + throws TException, IllegalPathException, IOException { + final String sg = "root.sg"; + final int storageGroupNum = 2; + + TSStatus status; + TSchemaNodeManagementReq nodeManagementReq; + TSchemaNodeManagementResp nodeManagementResp; + + // register DataNodes + registerDataNodes(); + + // set StorageGroups + for (int i = 0; i < storageGroupNum; i++) { + TSetStorageGroupReq setReq = new TSetStorageGroupReq(new TStorageGroupSchema(sg + i)); + status = processor.setStorageGroup(setReq); + Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + } + + ByteBuffer byteBuffer = generatePatternTreeBuffer(new String[] {"root"}); + nodeManagementReq = new TSchemaNodeManagementReq(byteBuffer, NodeManagementType.CHILD_PATHS); + nodeManagementResp = processor.getSchemaNodeManagementPartition(nodeManagementReq); + Assert.assertEquals( + TSStatusCode.SUCCESS_STATUS.getStatusCode(), nodeManagementResp.getStatus().getCode()); + Assert.assertEquals(2, nodeManagementResp.getMatchedNodeSize()); + Assert.assertNotNull(nodeManagementResp.getSchemaRegionMap()); + Assert.assertEquals(0, nodeManagementResp.getSchemaRegionMapSize()); + } } diff --git a/node-commons/src/main/java/org/apache/iotdb/commons/partition/SchemaNodeManagementPartition.java b/node-commons/src/main/java/org/apache/iotdb/commons/partition/SchemaNodeManagementPartition.java new file mode 100644 index 000000000000..abd1930f58e1 --- /dev/null +++ b/node-commons/src/main/java/org/apache/iotdb/commons/partition/SchemaNodeManagementPartition.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.commons.partition; + +import org.apache.iotdb.common.rpc.thrift.TRegionReplicaSet; +import org.apache.iotdb.common.rpc.thrift.TSeriesPartitionSlot; + +import java.util.Map; +import java.util.Set; + +public class SchemaNodeManagementPartition { + SchemaPartition schemaPartition; + + Set matchedNode; + + public SchemaNodeManagementPartition( + Map> schemaPartitionMap, + String seriesSlotExecutorName, + int seriesPartitionSlotNum, + Set matchedNode) { + this.schemaPartition = + new SchemaPartition(schemaPartitionMap, seriesSlotExecutorName, seriesPartitionSlotNum); + this.matchedNode = matchedNode; + } + + public SchemaPartition getSchemaPartition() { + return schemaPartition; + } + + public void setSchemaPartition(SchemaPartition schemaPartition) { + this.schemaPartition = schemaPartition; + } + + public Set getMatchedNode() { + return matchedNode; + } + + public void setMatchedNode(Set matchedNode) { + this.matchedNode = matchedNode; + } +} diff --git a/node-commons/src/main/java/org/apache/iotdb/commons/partition/SchemaPartition.java b/node-commons/src/main/java/org/apache/iotdb/commons/partition/SchemaPartition.java index e047805edd7a..2b05700add38 100644 --- a/node-commons/src/main/java/org/apache/iotdb/commons/partition/SchemaPartition.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/partition/SchemaPartition.java @@ -129,6 +129,23 @@ public SchemaPartition getSchemaPartition( } } + /** + * Get SchemaPartition by storageGroup name + * + * @param matchedStorageGroup List + * @return Subset of current SchemaPartition which contains matchedStorageGroup + */ + public SchemaPartition getSchemaPartition(List matchedStorageGroup) { + Map> result = new HashMap<>(); + matchedStorageGroup.forEach( + (storageGroup) -> { + if (schemaPartitionMap.containsKey(storageGroup)) { + result.put(storageGroup, new HashMap<>(schemaPartitionMap.get(storageGroup))); + } + }); + return new SchemaPartition(result, seriesSlotExecutorName, seriesPartitionSlotNum); + } + /** * Filter out unassigned PartitionSlots * diff --git a/server/src/main/java/org/apache/iotdb/db/client/ConfigNodeClient.java b/server/src/main/java/org/apache/iotdb/db/client/ConfigNodeClient.java index f1494eeb85a3..f1a9f40bdaf1 100644 --- a/server/src/main/java/org/apache/iotdb/db/client/ConfigNodeClient.java +++ b/server/src/main/java/org/apache/iotdb/db/client/ConfigNodeClient.java @@ -44,6 +44,8 @@ import org.apache.iotdb.confignode.rpc.thrift.TDeleteStorageGroupReq; import org.apache.iotdb.confignode.rpc.thrift.TDeleteStorageGroupsReq; import org.apache.iotdb.confignode.rpc.thrift.TLoginReq; +import org.apache.iotdb.confignode.rpc.thrift.TSchemaNodeManagementReq; +import org.apache.iotdb.confignode.rpc.thrift.TSchemaNodeManagementResp; import org.apache.iotdb.confignode.rpc.thrift.TSchemaPartitionReq; import org.apache.iotdb.confignode.rpc.thrift.TSchemaPartitionResp; import org.apache.iotdb.confignode.rpc.thrift.TSetDataReplicationFactorReq; @@ -440,6 +442,23 @@ public TSchemaPartitionResp getOrCreateSchemaPartition(TSchemaPartitionReq req) throw new TException(MSG_RECONNECTION_FAIL); } + @Override + public TSchemaNodeManagementResp getSchemaNodeManagementPartition(TSchemaNodeManagementReq req) + throws TException { + for (int i = 0; i < RETRY_NUM; i++) { + try { + TSchemaNodeManagementResp resp = client.getSchemaNodeManagementPartition(req); + if (!updateConfigNodeLeader(resp.status)) { + return resp; + } + } catch (TException e) { + configLeader = null; + } + reconnect(); + } + throw new TException(MSG_RECONNECTION_FAIL); + } + @Override public TDataPartitionResp getDataPartition(TDataPartitionReq req) throws TException { for (int i = 0; i < RETRY_NUM; i++) { diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/common/header/HeaderConstant.java b/server/src/main/java/org/apache/iotdb/db/mpp/common/header/HeaderConstant.java index 744351b58006..875bb5042803 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/common/header/HeaderConstant.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/common/header/HeaderConstant.java @@ -45,6 +45,8 @@ public class HeaderConstant { public static final String COLUMN_SCHEMA_REPLICATION_FACTOR = "schema_replication_factor"; public static final String COLUMN_DATA_REPLICATION_FACTOR = "data_replication_factor"; public static final String COLUMN_TIME_PARTITION_INTERVAL = "time_partition_interval"; + public static final String COLUMN_CHILDPATHS = "child paths"; + public static final String COLUMN_CHILDNODES = "child nodes"; // column names for count statement public static final String COLUMN_COLUMN = "column"; @@ -59,6 +61,8 @@ public class HeaderConstant { public static final DatasetHeader showDevicesWithSgHeader; public static final DatasetHeader showStorageGroupHeader; public static final DatasetHeader showTTLHeader; + public static final DatasetHeader showChildPathsHeader; + public static final DatasetHeader showChildNodesHeader; // dataset header for count statement public static final DatasetHeader countStorageGroupHeader; @@ -134,5 +138,11 @@ public class HeaderConstant { new ColumnHeader(COLUMN_STORAGE_GROUP, TSDataType.TEXT), new ColumnHeader(COLUMN_TTL, TSDataType.INT64)), true); + showChildPathsHeader = + new DatasetHeader( + Arrays.asList(new ColumnHeader(COLUMN_CHILDPATHS, TSDataType.TEXT)), true); + showChildNodesHeader = + new DatasetHeader( + Arrays.asList(new ColumnHeader(COLUMN_CHILDNODES, TSDataType.TEXT)), true); } } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/schema/ChildNodesSchemaScanOperator.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/schema/ChildNodesSchemaScanOperator.java new file mode 100644 index 000000000000..f8af06919725 --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/schema/ChildNodesSchemaScanOperator.java @@ -0,0 +1,93 @@ +/* + * 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.iotdb.db.mpp.execution.operator.schema; + +import org.apache.iotdb.commons.exception.MetadataException; +import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.db.mpp.common.header.HeaderConstant; +import org.apache.iotdb.db.mpp.execution.driver.SchemaDriverContext; +import org.apache.iotdb.db.mpp.execution.operator.OperatorContext; +import org.apache.iotdb.db.mpp.execution.operator.source.SourceOperator; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId; +import org.apache.iotdb.tsfile.read.common.block.TsBlock; +import org.apache.iotdb.tsfile.read.common.block.TsBlockBuilder; +import org.apache.iotdb.tsfile.utils.Binary; + +import java.util.Set; + +public class ChildNodesSchemaScanOperator implements SourceOperator { + private final PlanNodeId sourceId; + + private final OperatorContext operatorContext; + + private final PartialPath partialPath; + + private boolean isFinished; + + public ChildNodesSchemaScanOperator( + PlanNodeId sourceId, OperatorContext operatorContext, PartialPath partialPath) { + this.sourceId = sourceId; + this.operatorContext = operatorContext; + this.partialPath = partialPath; + } + + @Override + public OperatorContext getOperatorContext() { + return operatorContext; + } + + @Override + public TsBlock next() { + isFinished = true; + TsBlockBuilder tsBlockBuilder = + new TsBlockBuilder(HeaderConstant.showChildNodesHeader.getRespDataTypes()); + Set childNames; + try { + childNames = + ((SchemaDriverContext) operatorContext.getInstanceContext().getDriverContext()) + .getSchemaRegion() + .getChildNodeNameInNextLevel(partialPath); + } catch (MetadataException e) { + throw new RuntimeException(e.getMessage(), e); + } + childNames.forEach( + (path) -> { + tsBlockBuilder.getTimeColumnBuilder().writeLong(0L); + tsBlockBuilder.getColumnBuilder(0).writeBinary(new Binary(path)); + tsBlockBuilder.declarePosition(); + }); + return tsBlockBuilder.build(); + } + + @Override + public boolean hasNext() { + return !isFinished; + } + + @Override + public boolean isFinished() { + return isFinished; + } + + @Override + public PlanNodeId getSourceId() { + return sourceId; + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/schema/ChildPathsSchemaScanOperator.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/schema/ChildPathsSchemaScanOperator.java new file mode 100644 index 000000000000..ba187535ec42 --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/schema/ChildPathsSchemaScanOperator.java @@ -0,0 +1,93 @@ +/* + * 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.iotdb.db.mpp.execution.operator.schema; + +import org.apache.iotdb.commons.exception.MetadataException; +import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.db.mpp.common.header.HeaderConstant; +import org.apache.iotdb.db.mpp.execution.driver.SchemaDriverContext; +import org.apache.iotdb.db.mpp.execution.operator.OperatorContext; +import org.apache.iotdb.db.mpp.execution.operator.source.SourceOperator; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId; +import org.apache.iotdb.tsfile.read.common.block.TsBlock; +import org.apache.iotdb.tsfile.read.common.block.TsBlockBuilder; +import org.apache.iotdb.tsfile.utils.Binary; + +import java.util.Set; + +public class ChildPathsSchemaScanOperator implements SourceOperator { + private final PlanNodeId sourceId; + + private final OperatorContext operatorContext; + + private final PartialPath partialPath; + + private boolean isFinished; + + public ChildPathsSchemaScanOperator( + PlanNodeId sourceId, OperatorContext operatorContext, PartialPath partialPath) { + this.sourceId = sourceId; + this.operatorContext = operatorContext; + this.partialPath = partialPath; + } + + @Override + public OperatorContext getOperatorContext() { + return operatorContext; + } + + @Override + public TsBlock next() { + isFinished = true; + TsBlockBuilder tsBlockBuilder = + new TsBlockBuilder(HeaderConstant.showChildPathsHeader.getRespDataTypes()); + Set childPaths; + try { + childPaths = + ((SchemaDriverContext) operatorContext.getInstanceContext().getDriverContext()) + .getSchemaRegion() + .getChildNodePathInNextLevel(partialPath); + } catch (MetadataException e) { + throw new RuntimeException(e.getMessage(), e); + } + childPaths.forEach( + (path) -> { + tsBlockBuilder.getTimeColumnBuilder().writeLong(0L); + tsBlockBuilder.getColumnBuilder(0).writeBinary(new Binary(path)); + tsBlockBuilder.declarePosition(); + }); + return tsBlockBuilder.build(); + } + + @Override + public boolean hasNext() { + return !isFinished; + } + + @Override + public boolean isFinished() { + return isFinished; + } + + @Override + public PlanNodeId getSourceId() { + return sourceId; + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/schema/NodeManageMemoryMergeOperator.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/schema/NodeManageMemoryMergeOperator.java new file mode 100644 index 000000000000..f6833b8f353d --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/schema/NodeManageMemoryMergeOperator.java @@ -0,0 +1,102 @@ +/* + * 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.iotdb.db.mpp.execution.operator.schema; + +import org.apache.iotdb.confignode.rpc.thrift.NodeManagementType; +import org.apache.iotdb.db.mpp.common.header.HeaderConstant; +import org.apache.iotdb.db.mpp.execution.operator.Operator; +import org.apache.iotdb.db.mpp.execution.operator.OperatorContext; +import org.apache.iotdb.db.mpp.execution.operator.process.ProcessOperator; +import org.apache.iotdb.tsfile.read.common.block.TsBlock; +import org.apache.iotdb.tsfile.read.common.block.TsBlockBuilder; +import org.apache.iotdb.tsfile.utils.Binary; + +import com.google.common.util.concurrent.ListenableFuture; + +import java.util.Set; +import java.util.TreeSet; + +import static java.util.Objects.requireNonNull; + +public class NodeManageMemoryMergeOperator implements ProcessOperator { + private final OperatorContext operatorContext; + private Set data; + private final Operator child; + private NodeManagementType type; + private boolean isFinished; + + public NodeManageMemoryMergeOperator( + OperatorContext operatorContext, Set data, Operator child, NodeManagementType type) { + this.operatorContext = requireNonNull(operatorContext, "operatorContext is null"); + this.data = data; + this.child = requireNonNull(child, "child operator is null"); + this.type = type; + isFinished = false; + } + + @Override + public OperatorContext getOperatorContext() { + return operatorContext; + } + + @Override + public ListenableFuture isBlocked() { + return child.isBlocked(); + } + + @Override + public TsBlock next() { + isFinished = true; + TsBlock block = child.next(); + TsBlockBuilder tsBlockBuilder; + if (type == NodeManagementType.CHILD_PATHS) { + tsBlockBuilder = new TsBlockBuilder(HeaderConstant.showChildPathsHeader.getRespDataTypes()); + } else { + tsBlockBuilder = new TsBlockBuilder(HeaderConstant.showChildNodesHeader.getRespDataTypes()); + } + Set childPaths = new TreeSet<>(data); + for (int i = 0; i < block.getPositionCount(); i++) { + childPaths.add(block.getColumn(0).getBinary(i).toString()); + } + + childPaths.forEach( + path -> { + tsBlockBuilder.getTimeColumnBuilder().writeLong(0L); + tsBlockBuilder.getColumnBuilder(0).writeBinary(new Binary(path)); + tsBlockBuilder.declarePosition(); + }); + return tsBlockBuilder.build(); + } + + @Override + public boolean hasNext() { + return child.hasNext(); + } + + @Override + public void close() throws Exception { + child.close(); + } + + @Override + public boolean isFinished() { + return isFinished || child.isFinished(); + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analysis.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analysis.java index ee515b4bcf52..80f4d9ac9762 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analysis.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analysis.java @@ -95,6 +95,9 @@ public class Analysis { private boolean finishQueryAfterAnalyze; + // extra mesaage from config node, used for node management + private Set matchedNodes; + public Analysis() { this.finishQueryAfterAnalyze = false; } @@ -261,4 +264,12 @@ public void setDeviceToMeasurementIndexesMap( public Map> getDeviceToMeasurementIndexesMap() { return deviceToMeasurementIndexesMap; } + + public Set getMatchedNodes() { + return matchedNodes; + } + + public void setMatchedNodes(Set matchedNodes) { + this.matchedNodes = matchedNodes; + } } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analyzer.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analyzer.java index 97054bb3cd4d..83c80399e6ac 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analyzer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analyzer.java @@ -22,8 +22,10 @@ import org.apache.iotdb.commons.conf.IoTDBConstant; import org.apache.iotdb.commons.partition.DataPartition; import org.apache.iotdb.commons.partition.DataPartitionQueryParam; +import org.apache.iotdb.commons.partition.SchemaNodeManagementPartition; import org.apache.iotdb.commons.partition.SchemaPartition; import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.confignode.rpc.thrift.NodeManagementType; import org.apache.iotdb.db.exception.sql.SemanticException; import org.apache.iotdb.db.exception.sql.StatementAnalyzeException; import org.apache.iotdb.db.metadata.path.MeasurementPath; @@ -61,6 +63,8 @@ import org.apache.iotdb.db.mpp.plan.statement.metadata.CreateMultiTimeSeriesStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.CreateTimeSeriesStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.SchemaFetchStatement; +import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowChildNodesStatement; +import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowChildPathsStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowDevicesStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowStorageGroupStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowTTLStatement; @@ -1114,5 +1118,55 @@ public Analysis visitCountLevelTimeSeries( analysis.setRespDatasetHeader(HeaderConstant.countLevelTimeSeriesHeader); return analysis; } + + @Override + public Analysis visitShowChildPaths( + ShowChildPathsStatement showChildPathsStatement, MPPQueryContext context) { + Analysis analysis = new Analysis(); + analysis.setStatement(showChildPathsStatement); + + SchemaNodeManagementPartition schemaNodeManagementPartition = + partitionFetcher.getSchemaNodeManagementPartition( + new PathPatternTree(showChildPathsStatement.getPartialPath()), + NodeManagementType.CHILD_PATHS); + + if (schemaNodeManagementPartition == null) { + return analysis; + } + if (!schemaNodeManagementPartition.getMatchedNode().isEmpty() + && schemaNodeManagementPartition.getSchemaPartition().getSchemaPartitionMap().size() + == 0) { + analysis.setFinishQueryAfterAnalyze(true); + } + analysis.setMatchedNodes(schemaNodeManagementPartition.getMatchedNode()); + analysis.setSchemaPartitionInfo(schemaNodeManagementPartition.getSchemaPartition()); + analysis.setRespDatasetHeader(HeaderConstant.showChildPathsHeader); + return analysis; + } + + @Override + public Analysis visitShowChildNodes( + ShowChildNodesStatement showChildNodesStatement, MPPQueryContext context) { + Analysis analysis = new Analysis(); + analysis.setStatement(showChildNodesStatement); + + SchemaNodeManagementPartition schemaNodeManagementPartition = + partitionFetcher.getSchemaNodeManagementPartition( + new PathPatternTree(showChildNodesStatement.getPartialPath()), + NodeManagementType.CHILD_NODES); + + if (schemaNodeManagementPartition == null) { + return analysis; + } + if (!schemaNodeManagementPartition.getMatchedNode().isEmpty() + && schemaNodeManagementPartition.getSchemaPartition().getSchemaPartitionMap().size() + == 0) { + analysis.setFinishQueryAfterAnalyze(true); + } + analysis.setMatchedNodes(schemaNodeManagementPartition.getMatchedNode()); + analysis.setSchemaPartitionInfo(schemaNodeManagementPartition.getSchemaPartition()); + analysis.setRespDatasetHeader(HeaderConstant.showChildNodesHeader); + return analysis; + } } } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ClusterPartitionFetcher.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ClusterPartitionFetcher.java index 366c4add9a9b..874530b04caa 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ClusterPartitionFetcher.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ClusterPartitionFetcher.java @@ -26,11 +26,15 @@ import org.apache.iotdb.commons.exception.MetadataException; import org.apache.iotdb.commons.partition.DataPartition; import org.apache.iotdb.commons.partition.DataPartitionQueryParam; +import org.apache.iotdb.commons.partition.SchemaNodeManagementPartition; import org.apache.iotdb.commons.partition.SchemaPartition; import org.apache.iotdb.commons.partition.executor.SeriesPartitionExecutor; import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.confignode.rpc.thrift.NodeManagementType; import org.apache.iotdb.confignode.rpc.thrift.TDataPartitionReq; import org.apache.iotdb.confignode.rpc.thrift.TDataPartitionResp; +import org.apache.iotdb.confignode.rpc.thrift.TSchemaNodeManagementReq; +import org.apache.iotdb.confignode.rpc.thrift.TSchemaNodeManagementResp; import org.apache.iotdb.confignode.rpc.thrift.TSchemaPartitionReq; import org.apache.iotdb.confignode.rpc.thrift.TSchemaPartitionResp; import org.apache.iotdb.confignode.rpc.thrift.TSetStorageGroupReq; @@ -146,6 +150,23 @@ public SchemaPartition getOrCreateSchemaPartition(PathPatternTree patternTree) { } } + @Override + public SchemaNodeManagementPartition getSchemaNodeManagementPartition( + PathPatternTree patternTree, NodeManagementType type) { + try (ConfigNodeClient client = + configNodeClientManager.borrowClient(ConfigNodeInfo.partitionRegionId)) { + patternTree.constructTree(); + TSchemaNodeManagementResp schemaNodeManagementResp = + client.getSchemaNodeManagementPartition( + constructSchemaNodeManagementPartitionReq(patternTree, type)); + + return parseSchemaNodeManagementPartitionResp(schemaNodeManagementResp); + } catch (TException | IOException e) { + throw new StatementAnalyzeException( + "An error occurred when executing getSchemaNodeManagementPartition():" + e.getMessage()); + } + } + @Override public DataPartition getDataPartition( Map> sgNameToQueryParamsMap) { @@ -338,6 +359,20 @@ private TSchemaPartitionReq constructSchemaPartitionReq(PathPatternTree patternT } } + private TSchemaNodeManagementReq constructSchemaNodeManagementPartitionReq( + PathPatternTree patternTree, NodeManagementType type) { + PublicBAOS baos = new PublicBAOS(); + try { + patternTree.serialize(baos); + ByteBuffer serializedPatternTree = ByteBuffer.allocate(baos.size()); + serializedPatternTree.put(baos.getBuf(), 0, baos.size()); + serializedPatternTree.flip(); + return new TSchemaNodeManagementReq(serializedPatternTree, type); + } catch (IOException e) { + throw new StatementAnalyzeException("An error occurred when serializing pattern tree"); + } + } + private TDataPartitionReq constructDataPartitionReq( Map> sgNameToQueryParamsMap) { Map>> partitionSlotsMap = @@ -367,6 +402,15 @@ private SchemaPartition parseSchemaPartitionResp(TSchemaPartitionResp schemaPart IoTDBDescriptor.getInstance().getConfig().getSeriesPartitionSlotNum()); } + private SchemaNodeManagementPartition parseSchemaNodeManagementPartitionResp( + TSchemaNodeManagementResp schemaNodeManagementResp) { + return new SchemaNodeManagementPartition( + schemaNodeManagementResp.getSchemaRegionMap(), + IoTDBDescriptor.getInstance().getConfig().getSeriesPartitionExecutorClass(), + IoTDBDescriptor.getInstance().getConfig().getSeriesPartitionSlotNum(), + schemaNodeManagementResp.getMatchedNode()); + } + private DataPartition parseDataPartitionResp(TDataPartitionResp dataPartitionResp) { return new DataPartition( dataPartitionResp.getDataPartitionMap(), diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/FakePartitionFetcherImpl.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/FakePartitionFetcherImpl.java index f7ad58bca139..24add1c3c6b5 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/FakePartitionFetcherImpl.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/FakePartitionFetcherImpl.java @@ -28,7 +28,9 @@ import org.apache.iotdb.common.rpc.thrift.TTimePartitionSlot; import org.apache.iotdb.commons.partition.DataPartition; import org.apache.iotdb.commons.partition.DataPartitionQueryParam; +import org.apache.iotdb.commons.partition.SchemaNodeManagementPartition; import org.apache.iotdb.commons.partition.SchemaPartition; +import org.apache.iotdb.confignode.rpc.thrift.NodeManagementType; import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.mpp.common.schematree.PathPatternTree; @@ -50,6 +52,12 @@ public SchemaPartition getOrCreateSchemaPartition(PathPatternTree patternTree) { return null; } + @Override + public SchemaNodeManagementPartition getSchemaNodeManagementPartition( + PathPatternTree patternTree, NodeManagementType type) { + return null; + } + @Override public DataPartition getDataPartition( Map> sgNameToQueryParamsMap) { diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/IPartitionFetcher.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/IPartitionFetcher.java index 8c3ff4633f9e..b6a6dce930f9 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/IPartitionFetcher.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/IPartitionFetcher.java @@ -20,7 +20,9 @@ import org.apache.iotdb.commons.partition.DataPartition; import org.apache.iotdb.commons.partition.DataPartitionQueryParam; +import org.apache.iotdb.commons.partition.SchemaNodeManagementPartition; import org.apache.iotdb.commons.partition.SchemaPartition; +import org.apache.iotdb.confignode.rpc.thrift.NodeManagementType; import org.apache.iotdb.db.mpp.common.schematree.PathPatternTree; import java.util.List; @@ -32,6 +34,9 @@ public interface IPartitionFetcher { SchemaPartition getOrCreateSchemaPartition(PathPatternTree patternTree); + SchemaNodeManagementPartition getSchemaNodeManagementPartition( + PathPatternTree patternTree, NodeManagementType type); + DataPartition getDataPartition(Map> sgNameToQueryParamsMap); DataPartition getDataPartition(List dataPartitionQueryParams); diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/StandalonePartitionFetcher.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/StandalonePartitionFetcher.java index 562fea6fc158..34d2c545fbd9 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/StandalonePartitionFetcher.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/StandalonePartitionFetcher.java @@ -26,8 +26,10 @@ import org.apache.iotdb.commons.exception.MetadataException; import org.apache.iotdb.commons.partition.DataPartition; import org.apache.iotdb.commons.partition.DataPartitionQueryParam; +import org.apache.iotdb.commons.partition.SchemaNodeManagementPartition; import org.apache.iotdb.commons.partition.SchemaPartition; import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.confignode.rpc.thrift.NodeManagementType; import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.engine.StorageEngineV2; import org.apache.iotdb.db.exception.DataRegionException; @@ -65,6 +67,12 @@ public SchemaPartition getOrCreateSchemaPartition(PathPatternTree patternTree) { return null; } + @Override + public SchemaNodeManagementPartition getSchemaNodeManagementPartition( + PathPatternTree patternTree, NodeManagementType type) { + return null; + } + @Override public DataPartition getDataPartition( Map> sgNameToQueryParamsMap) { diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/memory/StatementMemorySourceVisitor.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/memory/StatementMemorySourceVisitor.java index aa7ebeff6150..220a952189ff 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/memory/StatementMemorySourceVisitor.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/memory/StatementMemorySourceVisitor.java @@ -20,11 +20,18 @@ package org.apache.iotdb.db.mpp.plan.execution.memory; import org.apache.iotdb.db.mpp.common.header.DatasetHeader; +import org.apache.iotdb.db.mpp.common.header.HeaderConstant; import org.apache.iotdb.db.mpp.plan.statement.StatementNode; import org.apache.iotdb.db.mpp.plan.statement.StatementVisitor; +import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowChildNodesStatement; +import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowChildPathsStatement; import org.apache.iotdb.tsfile.read.common.block.TsBlock; +import org.apache.iotdb.tsfile.read.common.block.TsBlockBuilder; +import org.apache.iotdb.tsfile.utils.Binary; import java.util.ArrayList; +import java.util.Set; +import java.util.TreeSet; public class StatementMemorySourceVisitor extends StatementVisitor { @@ -33,4 +40,36 @@ public class StatementMemorySourceVisitor public StatementMemorySource visitNode(StatementNode node, StatementMemorySourceContext context) { return new StatementMemorySource(new TsBlock(0), new DatasetHeader(new ArrayList<>(), false)); } + + @Override + public StatementMemorySource visitShowChildPaths( + ShowChildPathsStatement showChildPathsStatement, StatementMemorySourceContext context) { + TsBlockBuilder tsBlockBuilder = + new TsBlockBuilder(HeaderConstant.showChildPathsHeader.getRespDataTypes()); + Set matchedChildPaths = new TreeSet<>(context.getAnalysis().getMatchedNodes()); + matchedChildPaths.forEach( + path -> { + tsBlockBuilder.getTimeColumnBuilder().writeLong(0L); + tsBlockBuilder.getColumnBuilder(0).writeBinary(new Binary(path)); + tsBlockBuilder.declarePosition(); + }); + return new StatementMemorySource( + tsBlockBuilder.build(), context.getAnalysis().getRespDatasetHeader()); + } + + @Override + public StatementMemorySource visitShowChildNodes( + ShowChildNodesStatement showChildNodesStatement, StatementMemorySourceContext context) { + TsBlockBuilder tsBlockBuilder = + new TsBlockBuilder(HeaderConstant.showChildNodesHeader.getRespDataTypes()); + Set matchedChildNodes = new TreeSet<>(context.getAnalysis().getMatchedNodes()); + matchedChildNodes.forEach( + node -> { + tsBlockBuilder.getTimeColumnBuilder().writeLong(0L); + tsBlockBuilder.getColumnBuilder(0).writeBinary(new Binary(node)); + tsBlockBuilder.declarePosition(); + }); + return new StatementMemorySource( + tsBlockBuilder.build(), context.getAnalysis().getRespDatasetHeader()); + } } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java index 8d826f98f2d3..66880a0a64d3 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java @@ -58,6 +58,8 @@ import org.apache.iotdb.db.mpp.plan.statement.metadata.DeleteStorageGroupStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.SetStorageGroupStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.SetTTLStatement; +import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowChildNodesStatement; +import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowChildPathsStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowDevicesStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowStorageGroupStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowTTLStatement; @@ -475,6 +477,26 @@ public Statement visitCountStorageGroup(CountStorageGroupContext ctx) { return new CountStorageGroupStatement(path); } + // Show Child Paths ===================================================================== + @Override + public Statement visitShowChildPaths(IoTDBSqlParser.ShowChildPathsContext ctx) { + if (ctx.prefixPath() != null) { + return new ShowChildPathsStatement(parsePrefixPath(ctx.prefixPath())); + } else { + return new ShowChildPathsStatement(new PartialPath(SQLConstant.getSingleRootArray())); + } + } + + // Show Child Nodes ===================================================================== + @Override + public Statement visitShowChildNodes(IoTDBSqlParser.ShowChildNodesContext ctx) { + if (ctx.prefixPath() != null) { + return new ShowChildNodesStatement(parsePrefixPath(ctx.prefixPath())); + } else { + return new ShowChildNodesStatement(new PartialPath(SQLConstant.getSingleRootArray())); + } + } + /** Data Manipulation Language (DML) */ // Select Statement ======================================================================== diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LocalExecutionPlanner.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LocalExecutionPlanner.java index 56ab2028057b..689745cb9180 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LocalExecutionPlanner.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LocalExecutionPlanner.java @@ -74,10 +74,13 @@ import org.apache.iotdb.db.mpp.execution.operator.process.merge.NonOverlappedMultiColumnMerger; import org.apache.iotdb.db.mpp.execution.operator.process.merge.SingleColumnMerger; import org.apache.iotdb.db.mpp.execution.operator.process.merge.TimeComparator; +import org.apache.iotdb.db.mpp.execution.operator.schema.ChildNodesSchemaScanOperator; +import org.apache.iotdb.db.mpp.execution.operator.schema.ChildPathsSchemaScanOperator; import org.apache.iotdb.db.mpp.execution.operator.schema.CountMergeOperator; import org.apache.iotdb.db.mpp.execution.operator.schema.DevicesCountOperator; import org.apache.iotdb.db.mpp.execution.operator.schema.DevicesSchemaScanOperator; import org.apache.iotdb.db.mpp.execution.operator.schema.LevelTimeSeriesCountOperator; +import org.apache.iotdb.db.mpp.execution.operator.schema.NodeManageMemoryMergeOperator; import org.apache.iotdb.db.mpp.execution.operator.schema.SchemaFetchMergeOperator; import org.apache.iotdb.db.mpp.execution.operator.schema.SchemaFetchScanOperator; import org.apache.iotdb.db.mpp.execution.operator.schema.SchemaQueryMergeOperator; @@ -91,10 +94,13 @@ import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanVisitor; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.ChildNodesSchemaScanNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.ChildPathsSchemaScanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.CountSchemaMergeNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.DevicesCountNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.DevicesSchemaScanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.LevelTimeSeriesCountNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.NodeManagementMemoryMergeNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.SchemaFetchMergeNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.SchemaFetchScanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.SchemaQueryMergeNode; @@ -271,6 +277,10 @@ public Operator visitSchemaQueryScan( return visitTimeSeriesCount((TimeSeriesCountNode) node, context); } else if (node instanceof LevelTimeSeriesCountNode) { return visitLevelTimeSeriesCount((LevelTimeSeriesCountNode) node, context); + } else if (node instanceof ChildPathsSchemaScanNode) { + return visitChildPathsSchemaScan((ChildPathsSchemaScanNode) node, context); + } else if (node instanceof ChildNodesSchemaScanNode) { + return visitChildNodesSchemaScan((ChildNodesSchemaScanNode) node, context); } return visitPlan(node, context); } @@ -382,6 +392,44 @@ public Operator visitLevelTimeSeriesCount( node.getLevel()); } + @Override + public Operator visitChildPathsSchemaScan( + ChildPathsSchemaScanNode node, LocalExecutionPlanContext context) { + OperatorContext operatorContext = + context.instanceContext.addOperatorContext( + context.getNextOperatorId(), + node.getPlanNodeId(), + ChildPathsSchemaScanNode.class.getSimpleName()); + return new ChildPathsSchemaScanOperator( + node.getPlanNodeId(), operatorContext, node.getPrefixPath()); + } + + @Override + public Operator visitChildNodesSchemaScan( + ChildNodesSchemaScanNode node, LocalExecutionPlanContext context) { + OperatorContext operatorContext = + context.instanceContext.addOperatorContext( + context.getNextOperatorId(), + node.getPlanNodeId(), + ChildNodesSchemaScanNode.class.getSimpleName()); + return new ChildNodesSchemaScanOperator( + node.getPlanNodeId(), operatorContext, node.getPrefixPath()); + } + + @Override + public Operator visitNodeManagementMemoryMerge( + NodeManagementMemoryMergeNode node, LocalExecutionPlanContext context) { + Operator child = node.getChild().accept(this, context); + return new NodeManageMemoryMergeOperator( + context.instanceContext.addOperatorContext( + context.getNextOperatorId(), + node.getPlanNodeId(), + NodeManageMemoryMergeOperator.class.getSimpleName()), + node.getData(), + child, + node.getType()); + } + @Override public Operator visitSeriesAggregationScan( SeriesAggregationScanNode node, LocalExecutionPlanContext context) { diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanBuilder.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanBuilder.java index b0c1c4844192..3b8bbf87eda4 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanBuilder.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanBuilder.java @@ -21,6 +21,7 @@ import org.apache.iotdb.commons.exception.IllegalPathException; import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.confignode.rpc.thrift.NodeManagementType; import org.apache.iotdb.db.metadata.path.AlignedPath; import org.apache.iotdb.db.metadata.path.MeasurementPath; import org.apache.iotdb.db.metadata.utils.MetaUtils; @@ -29,10 +30,13 @@ import org.apache.iotdb.db.mpp.plan.analyze.ExpressionAnalyzer; import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.ChildNodesSchemaScanNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.ChildPathsSchemaScanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.CountSchemaMergeNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.DevicesCountNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.DevicesSchemaScanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.LevelTimeSeriesCountNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.NodeManagementMemoryMergeNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.SchemaFetchMergeNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.SchemaFetchScanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.SchemaQueryMergeNode; @@ -581,4 +585,23 @@ public LogicalPlanBuilder planLevelTimeSeriesCountSource( context.getQueryId().genPlanNodeId(), partialPath, prefixPath, level); return this; } + + public LogicalPlanBuilder planChildPathsSchemaSource(PartialPath partialPath) { + this.root = new ChildPathsSchemaScanNode(context.getQueryId().genPlanNodeId(), partialPath); + return this; + } + + public LogicalPlanBuilder planChildNodesSchemaSource(PartialPath partialPath) { + this.root = new ChildNodesSchemaScanNode(context.getQueryId().genPlanNodeId(), partialPath); + return this; + } + + public LogicalPlanBuilder planNodeManagementMemoryMerge( + Set data, NodeManagementType type) { + NodeManagementMemoryMergeNode memorySourceNode = + new NodeManagementMemoryMergeNode(context.getQueryId().genPlanNodeId(), data, type); + memorySourceNode.addChild(this.getRoot()); + this.root = memorySourceNode; + return this; + } } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanner.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanner.java index 57d0854b953c..a4628d6ef2c0 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanner.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanner.java @@ -18,6 +18,7 @@ */ package org.apache.iotdb.db.mpp.plan.planner; +import org.apache.iotdb.confignode.rpc.thrift.NodeManagementType; import org.apache.iotdb.db.mpp.common.MPPQueryContext; import org.apache.iotdb.db.mpp.plan.analyze.Analysis; import org.apache.iotdb.db.mpp.plan.optimization.PlanOptimizer; @@ -49,6 +50,8 @@ import org.apache.iotdb.db.mpp.plan.statement.metadata.CreateMultiTimeSeriesStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.CreateTimeSeriesStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.SchemaFetchStatement; +import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowChildNodesStatement; +import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowChildPathsStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowDevicesStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowTimeSeriesStatement; import org.apache.iotdb.db.query.expression.Expression; @@ -503,5 +506,27 @@ public PlanNode visitSchemaFetch( schemaFetchStatement.getPatternTree()) .getRoot(); } + + @Override + public PlanNode visitShowChildPaths( + ShowChildPathsStatement showChildPathsStatement, MPPQueryContext context) { + LogicalPlanBuilder planBuilder = new LogicalPlanBuilder(context); + return planBuilder + .planChildPathsSchemaSource(showChildPathsStatement.getPartialPath()) + .planSchemaQueryMerge(false) + .planNodeManagementMemoryMerge(analysis.getMatchedNodes(), NodeManagementType.CHILD_PATHS) + .getRoot(); + } + + @Override + public PlanNode visitShowChildNodes( + ShowChildNodesStatement showChildNodesStatement, MPPQueryContext context) { + LogicalPlanBuilder planBuilder = new LogicalPlanBuilder(context); + return planBuilder + .planChildNodesSchemaSource(showChildNodesStatement.getPartialPath()) + .planSchemaQueryMerge(false) + .planNodeManagementMemoryMerge(analysis.getMatchedNodes(), NodeManagementType.CHILD_NODES) + .getRoot(); + } } } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/PlanNodeType.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/PlanNodeType.java index 4fe62bad8263..5daa489ea99d 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/PlanNodeType.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/PlanNodeType.java @@ -19,10 +19,13 @@ package org.apache.iotdb.db.mpp.plan.planner.plan.node; import org.apache.iotdb.commons.exception.IllegalPathException; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.ChildNodesSchemaScanNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.ChildPathsSchemaScanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.CountSchemaMergeNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.DevicesCountNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.DevicesSchemaScanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.LevelTimeSeriesCountNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.NodeManagementMemoryMergeNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.SchemaFetchMergeNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.SchemaFetchScanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.SchemaQueryMergeNode; @@ -101,7 +104,10 @@ public enum PlanNodeType { SCHEMA_FETCH_MERGE((short) 36), TRANSFORM((short) 37), DELETE_REGION((short) 38), - CREATE_MULTI_TIME_SERIES((short) 39); + CREATE_MULTI_TIME_SERIES((short) 39), + CHILD_PATHS_SCAN((short) 40), + CHILD_NODES_SCAN((short) 41), + NODE_MANAGEMENT_MEMORY_MERGE((short) 42); private final short nodeType; @@ -205,6 +211,12 @@ public static PlanNode deserialize(ByteBuffer buffer) { return DeleteRegionNode.deserialize(buffer); case 39: return CreateMultiTimeSeriesNode.deserialize(buffer); + case 40: + return ChildPathsSchemaScanNode.deserialize(buffer); + case 41: + return ChildNodesSchemaScanNode.deserialize(buffer); + case 42: + return NodeManagementMemoryMergeNode.deserialize(buffer); default: throw new IllegalArgumentException("Invalid node type: " + nodeType); } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/PlanVisitor.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/PlanVisitor.java index 4d552df27b23..2cab8c04d4aa 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/PlanVisitor.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/PlanVisitor.java @@ -18,10 +18,13 @@ */ package org.apache.iotdb.db.mpp.plan.planner.plan.node; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.ChildNodesSchemaScanNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.ChildPathsSchemaScanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.CountSchemaMergeNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.DevicesCountNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.DevicesSchemaScanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.LevelTimeSeriesCountNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.NodeManagementMemoryMergeNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.SchemaFetchMergeNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.SchemaFetchScanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.SchemaQueryMergeNode; @@ -225,4 +228,16 @@ public R visitInsertMultiTablets(InsertMultiTabletsNode node, C context) { public R visitInsertRowsOfOneDevice(InsertRowsOfOneDeviceNode node, C context) { return visitPlan(node, context); } + + public R visitChildPathsSchemaScan(ChildPathsSchemaScanNode node, C context) { + return visitPlan(node, context); + } + + public R visitChildNodesSchemaScan(ChildNodesSchemaScanNode node, C context) { + return visitPlan(node, context); + } + + public R visitNodeManagementMemoryMerge(NodeManagementMemoryMergeNode node, C context) { + return visitPlan(node, context); + } } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/metedata/read/ChildNodesSchemaScanNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/metedata/read/ChildNodesSchemaScanNode.java new file mode 100644 index 000000000000..ca42cf4cee7d --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/metedata/read/ChildNodesSchemaScanNode.java @@ -0,0 +1,87 @@ +/* + * 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.iotdb.db.mpp.plan.planner.plan.node.metedata.read; + +import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.db.metadata.path.PathDeserializeUtil; +import org.apache.iotdb.db.mpp.common.header.HeaderConstant; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeType; + +import java.nio.ByteBuffer; +import java.util.List; +import java.util.Objects; + +public class ChildNodesSchemaScanNode extends SchemaQueryScanNode { + // the path could be a prefix path with wildcard + private PartialPath prefixPath; + + public ChildNodesSchemaScanNode(PlanNodeId id, PartialPath prefixPath) { + super(id); + this.prefixPath = prefixPath; + } + + public PartialPath getPrefixPath() { + return prefixPath; + } + + @Override + public PlanNode clone() { + return new ChildNodesSchemaScanNode(getPlanNodeId(), prefixPath); + } + + @Override + public List getOutputColumnNames() { + return HeaderConstant.showChildNodesHeader.getRespColumns(); + } + + @Override + protected void serializeAttributes(ByteBuffer byteBuffer) { + PlanNodeType.CHILD_NODES_SCAN.serialize(byteBuffer); + prefixPath.serialize(byteBuffer); + } + + public static PlanNode deserialize(ByteBuffer buffer) { + PartialPath path = (PartialPath) PathDeserializeUtil.deserialize(buffer); + PlanNodeId planNodeId = PlanNodeId.deserialize(buffer); + return new ChildNodesSchemaScanNode(planNodeId, path); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + ChildNodesSchemaScanNode that = (ChildNodesSchemaScanNode) o; + return prefixPath == that.prefixPath; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), prefixPath); + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/metedata/read/ChildPathsSchemaScanNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/metedata/read/ChildPathsSchemaScanNode.java new file mode 100644 index 000000000000..db6218b2013c --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/metedata/read/ChildPathsSchemaScanNode.java @@ -0,0 +1,87 @@ +/* + * 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.iotdb.db.mpp.plan.planner.plan.node.metedata.read; + +import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.db.metadata.path.PathDeserializeUtil; +import org.apache.iotdb.db.mpp.common.header.HeaderConstant; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeType; + +import java.nio.ByteBuffer; +import java.util.List; +import java.util.Objects; + +public class ChildPathsSchemaScanNode extends SchemaQueryScanNode { + // the path could be a prefix path with wildcard + private PartialPath prefixPath; + + public ChildPathsSchemaScanNode(PlanNodeId id, PartialPath prefixPath) { + super(id); + this.prefixPath = prefixPath; + } + + public PartialPath getPrefixPath() { + return prefixPath; + } + + @Override + public PlanNode clone() { + return new ChildPathsSchemaScanNode(getPlanNodeId(), prefixPath); + } + + @Override + public List getOutputColumnNames() { + return HeaderConstant.showChildPathsHeader.getRespColumns(); + } + + @Override + protected void serializeAttributes(ByteBuffer byteBuffer) { + PlanNodeType.CHILD_PATHS_SCAN.serialize(byteBuffer); + prefixPath.serialize(byteBuffer); + } + + public static PlanNode deserialize(ByteBuffer buffer) { + PartialPath path = (PartialPath) PathDeserializeUtil.deserialize(buffer); + PlanNodeId planNodeId = PlanNodeId.deserialize(buffer); + return new ChildPathsSchemaScanNode(planNodeId, path); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + ChildPathsSchemaScanNode that = (ChildPathsSchemaScanNode) o; + return prefixPath == that.prefixPath; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), prefixPath); + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/metedata/read/NodeManagementMemoryMergeNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/metedata/read/NodeManagementMemoryMergeNode.java new file mode 100644 index 000000000000..c202a120ed60 --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/metedata/read/NodeManagementMemoryMergeNode.java @@ -0,0 +1,115 @@ +/* + * 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.iotdb.db.mpp.plan.planner.plan.node.metedata.read; + +import org.apache.iotdb.confignode.rpc.thrift.NodeManagementType; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeType; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanVisitor; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.process.ProcessNode; +import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils; + +import com.google.common.collect.ImmutableList; + +import java.nio.ByteBuffer; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class NodeManagementMemoryMergeNode extends ProcessNode { + private final Set data; + + private PlanNode child; + + private NodeManagementType type; + + public NodeManagementMemoryMergeNode(PlanNodeId id, Set data, NodeManagementType type) { + super(id); + this.data = data; + this.type = type; + } + + public Set getData() { + return data; + } + + public NodeManagementType getType() { + return type; + } + + public void setType(NodeManagementType type) { + this.type = type; + } + + public PlanNode getChild() { + return child; + } + + @Override + public List getChildren() { + return ImmutableList.of(child); + } + + @Override + public void addChild(PlanNode child) { + this.child = child; + } + + @Override + public PlanNode clone() { + return new NodeManagementMemoryMergeNode(getPlanNodeId(), this.data, this.type); + } + + @Override + public int allowedChildCount() { + return ONE_CHILD; + } + + @Override + public List getOutputColumnNames() { + return child.getOutputColumnNames(); + } + + @Override + public R accept(PlanVisitor visitor, C context) { + return visitor.visitNodeManagementMemoryMerge(this, context); + } + + @Override + protected void serializeAttributes(ByteBuffer byteBuffer) { + PlanNodeType.NODE_MANAGEMENT_MEMORY_MERGE.serialize(byteBuffer); + int size = data.size(); + ReadWriteIOUtils.write(size, byteBuffer); + data.forEach(node -> ReadWriteIOUtils.write(node, byteBuffer)); + byteBuffer.put((byte) type.ordinal()); + } + + public static NodeManagementMemoryMergeNode deserialize(ByteBuffer byteBuffer) { + Set data = new HashSet<>(); + int size = byteBuffer.getInt(); + for (int i = 0; i < size; i++) { + data.add(ReadWriteIOUtils.readString(byteBuffer)); + } + NodeManagementType type = NodeManagementType.findByValue(byteBuffer.get()); + PlanNodeId planNodeId = PlanNodeId.deserialize(byteBuffer); + return new NodeManagementMemoryMergeNode(planNodeId, data, type); + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/StatementVisitor.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/StatementVisitor.java index 16bbdff8dde0..86daaafcc311 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/StatementVisitor.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/StatementVisitor.java @@ -38,6 +38,8 @@ import org.apache.iotdb.db.mpp.plan.statement.metadata.SchemaFetchStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.SetStorageGroupStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.SetTTLStatement; +import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowChildNodesStatement; +import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowChildPathsStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowDevicesStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowStorageGroupStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowTTLStatement; @@ -183,4 +185,12 @@ public R visitInsertRowsOfOneDevice( public R visitSchemaFetch(SchemaFetchStatement schemaFetchStatement, C context) { return visitStatement(schemaFetchStatement, context); } + + public R visitShowChildPaths(ShowChildPathsStatement showChildPathsStatement, C context) { + return visitStatement(showChildPathsStatement, context); + } + + public R visitShowChildNodes(ShowChildNodesStatement showChildNodesStatement, C context) { + return visitStatement(showChildNodesStatement, context); + } } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/metadata/ShowChildNodesStatement.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/metadata/ShowChildNodesStatement.java new file mode 100644 index 000000000000..0c7a44d68a75 --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/metadata/ShowChildNodesStatement.java @@ -0,0 +1,41 @@ +/* + * 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.iotdb.db.mpp.plan.statement.metadata; + +import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.db.mpp.plan.statement.StatementVisitor; + +public class ShowChildNodesStatement extends ShowStatement { + private final PartialPath partialPath; + + public ShowChildNodesStatement(PartialPath partialPath) { + super(); + this.partialPath = partialPath; + } + + public PartialPath getPartialPath() { + return partialPath; + } + + @Override + public R accept(StatementVisitor visitor, C context) { + return visitor.visitShowChildNodes(this, context); + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/metadata/ShowChildPathsStatement.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/metadata/ShowChildPathsStatement.java new file mode 100644 index 000000000000..28ec7cc6c94f --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/metadata/ShowChildPathsStatement.java @@ -0,0 +1,41 @@ +/* + * 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.iotdb.db.mpp.plan.statement.metadata; + +import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.db.mpp.plan.statement.StatementVisitor; + +public class ShowChildPathsStatement extends ShowStatement { + private final PartialPath partialPath; + + public ShowChildPathsStatement(PartialPath partialPath) { + super(); + this.partialPath = partialPath; + } + + public PartialPath getPartialPath() { + return partialPath; + } + + @Override + public R accept(StatementVisitor visitor, C context) { + return visitor.visitShowChildPaths(this, context); + } +} diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/LogicalPlannerTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/LogicalPlannerTest.java index a0b6261afb3f..7b5b3c5f3331 100644 --- a/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/LogicalPlannerTest.java +++ b/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/LogicalPlannerTest.java @@ -32,7 +32,10 @@ import org.apache.iotdb.db.mpp.plan.planner.LogicalPlanner; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeType; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.ChildNodesSchemaScanNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.ChildPathsSchemaScanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.DevicesSchemaScanNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.NodeManagementMemoryMergeNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.SchemaQueryMergeNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.TimeSeriesSchemaScanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.write.AlterTimeSeriesNode; @@ -541,6 +544,42 @@ public void testShowDevices() { } } + @Test + public void testShowChildPaths() { + String sql = "SHOW CHILD PATHS root.ln"; + try { + NodeManagementMemoryMergeNode memorySourceNode = + (NodeManagementMemoryMergeNode) parseSQLToPlanNode(sql); + SchemaQueryMergeNode schemaQueryMergeNode = + (SchemaQueryMergeNode) memorySourceNode.getChildren().get(0); + ChildPathsSchemaScanNode childPathsSchemaScanNode = + (ChildPathsSchemaScanNode) schemaQueryMergeNode.getChildren().get(0); + Assert.assertNotNull(childPathsSchemaScanNode); + Assert.assertEquals(new PartialPath("root.ln"), childPathsSchemaScanNode.getPrefixPath()); + } catch (Exception e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void testShowChildNodes() { + String sql = "SHOW CHILD NODES root.ln"; + try { + NodeManagementMemoryMergeNode memorySourceNode = + (NodeManagementMemoryMergeNode) parseSQLToPlanNode(sql); + SchemaQueryMergeNode schemaQueryMergeNode = + (SchemaQueryMergeNode) memorySourceNode.getChildren().get(0); + ChildNodesSchemaScanNode childNodesSchemaScanNode = + (ChildNodesSchemaScanNode) schemaQueryMergeNode.getChildren().get(0); + Assert.assertNotNull(childNodesSchemaScanNode); + Assert.assertEquals(new PartialPath("root.ln"), childNodesSchemaScanNode.getPrefixPath()); + } catch (Exception e) { + e.printStackTrace(); + fail(); + } + } + private PlanNode parseSQLToPlanNode(String sql) { PlanNode planNode = null; try { diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/metadata/read/NodeManagementMemoryMergeNodeSerdeTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/metadata/read/NodeManagementMemoryMergeNodeSerdeTest.java new file mode 100644 index 000000000000..d6dba5bd10f6 --- /dev/null +++ b/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/metadata/read/NodeManagementMemoryMergeNodeSerdeTest.java @@ -0,0 +1,111 @@ +/* + * 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.iotdb.db.mpp.plan.plan.node.metadata.read; + +import org.apache.iotdb.common.rpc.thrift.TEndPoint; +import org.apache.iotdb.commons.exception.IllegalPathException; +import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.confignode.rpc.thrift.NodeManagementType; +import org.apache.iotdb.db.mpp.common.FragmentInstanceId; +import org.apache.iotdb.db.mpp.common.PlanFragmentId; +import org.apache.iotdb.db.mpp.plan.plan.node.PlanNodeDeserializeHelper; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.ChildNodesSchemaScanNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.ChildPathsSchemaScanNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.NodeManagementMemoryMergeNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.SchemaQueryMergeNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.process.ExchangeNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.sink.FragmentSinkNode; + +import org.junit.Assert; +import org.junit.Test; + +import java.nio.ByteBuffer; +import java.util.HashSet; +import java.util.Set; + +public class NodeManagementMemoryMergeNodeSerdeTest { + + @Test + public void testChildPathsSerializeAndDeserialize() throws IllegalPathException { + Set data = new HashSet<>(); + data.add("root.ln"); + data.add("root.abc"); + NodeManagementMemoryMergeNode memorySourceNode = + new NodeManagementMemoryMergeNode( + new PlanNodeId("nodeManagementMerge"), data, NodeManagementType.CHILD_PATHS); + SchemaQueryMergeNode schemaMergeNode = new SchemaQueryMergeNode(new PlanNodeId("schemaMerge")); + ExchangeNode exchangeNode = new ExchangeNode(new PlanNodeId("exchange")); + ChildPathsSchemaScanNode childPathsSchemaScanNode = + new ChildPathsSchemaScanNode(new PlanNodeId("childPathsScan"), new PartialPath("root.ln")); + FragmentSinkNode fragmentSinkNode = new FragmentSinkNode(new PlanNodeId("fragmentSink")); + fragmentSinkNode.addChild(childPathsSchemaScanNode); + fragmentSinkNode.setDownStream( + new TEndPoint("127.0.0.1", 6667), + new FragmentInstanceId(new PlanFragmentId("q", 1), "ds"), + new PlanNodeId("test")); + exchangeNode.addChild(schemaMergeNode); + exchangeNode.setRemoteSourceNode(fragmentSinkNode); + exchangeNode.setUpstream( + new TEndPoint("127.0.0.1", 6667), + new FragmentInstanceId(new PlanFragmentId("q", 1), "ds"), + new PlanNodeId("test")); + memorySourceNode.addChild(exchangeNode); + ByteBuffer byteBuffer = ByteBuffer.allocate(1024); + memorySourceNode.serialize(byteBuffer); + byteBuffer.flip(); + NodeManagementMemoryMergeNode memorySourceNode1 = + (NodeManagementMemoryMergeNode) PlanNodeDeserializeHelper.deserialize(byteBuffer); + Assert.assertEquals(memorySourceNode, memorySourceNode1); + } + + @Test + public void testChildNodesSerializeAndDeserialize() throws IllegalPathException { + Set data = new HashSet<>(); + data.add("ln"); + data.add("abc"); + NodeManagementMemoryMergeNode memorySourceNode = + new NodeManagementMemoryMergeNode( + new PlanNodeId("nodeManagementMerge"), data, NodeManagementType.CHILD_NODES); + SchemaQueryMergeNode schemaMergeNode = new SchemaQueryMergeNode(new PlanNodeId("schemaMerge")); + ExchangeNode exchangeNode = new ExchangeNode(new PlanNodeId("exchange")); + ChildNodesSchemaScanNode childNodesSchemaScanNode = + new ChildNodesSchemaScanNode(new PlanNodeId("childNodesScan"), new PartialPath("root.ln")); + FragmentSinkNode fragmentSinkNode = new FragmentSinkNode(new PlanNodeId("fragmentSink")); + fragmentSinkNode.addChild(childNodesSchemaScanNode); + fragmentSinkNode.setDownStream( + new TEndPoint("127.0.0.1", 6667), + new FragmentInstanceId(new PlanFragmentId("q", 1), "ds"), + new PlanNodeId("test")); + exchangeNode.addChild(schemaMergeNode); + exchangeNode.setRemoteSourceNode(fragmentSinkNode); + exchangeNode.setUpstream( + new TEndPoint("127.0.0.1", 6667), + new FragmentInstanceId(new PlanFragmentId("q", 1), "ds"), + new PlanNodeId("test")); + memorySourceNode.addChild(exchangeNode); + ByteBuffer byteBuffer = ByteBuffer.allocate(1024); + memorySourceNode.serialize(byteBuffer); + byteBuffer.flip(); + NodeManagementMemoryMergeNode memorySourceNode1 = + (NodeManagementMemoryMergeNode) PlanNodeDeserializeHelper.deserialize(byteBuffer); + Assert.assertEquals(memorySourceNode, memorySourceNode1); + } +} diff --git a/thrift-confignode/src/main/thrift/confignode.thrift b/thrift-confignode/src/main/thrift/confignode.thrift index f102e2aa3a52..4b933e548fd0 100644 --- a/thrift-confignode/src/main/thrift/confignode.thrift +++ b/thrift-confignode/src/main/thrift/confignode.thrift @@ -117,6 +117,25 @@ struct TSchemaPartitionResp { 2: optional map> schemaRegionMap } +// Node Management + +enum NodeManagementType { +CHILD_PATHS, +CHILD_NODES +} + +struct TSchemaNodeManagementReq { + 1: required binary pathPatternTree + 2: required NodeManagementType type +} + +struct TSchemaNodeManagementResp { + 1: required common.TSStatus status + // map> + 2: optional map> schemaRegionMap + 3: optional set matchedNode +} + // Data struct TDataPartitionReq { // map>> @@ -208,6 +227,10 @@ service ConfigIService { TSchemaPartitionResp getOrCreateSchemaPartition(TSchemaPartitionReq req) + /* Node Management */ + + TSchemaNodeManagementResp getSchemaNodeManagementPartition(TSchemaNodeManagementReq req) + /* Data */ TDataPartitionResp getDataPartition(TDataPartitionReq req) From 9c506e4be3ec3515537d7be69dc6e245785a8ffc Mon Sep 17 00:00:00 2001 From: ZhangHongYin <46039728+SpriCoder@users.noreply.github.com> Date: Fri, 20 May 2022 12:28:14 +0800 Subject: [PATCH 056/436] [IOTDB-3241] Fix default value in common config (#5962) --- .../confignode/conf/ConfigNodeDescriptor.java | 1 - .../persistence/AuthorInfoTest.java | 6 ----- .../iotdb/commons/conf/CommonConfig.java | 25 +++++++++++++---- .../iotdb/commons/conf/CommonDescriptor.java | 1 - .../iotdb/commons/conf/IoTDBConstant.java | 3 +++ .../org/apache/iotdb/db/conf/IoTDBConfig.java | 27 +++++++++++-------- .../apache/iotdb/db/conf/IoTDBDescriptor.java | 1 - 7 files changed, 39 insertions(+), 25 deletions(-) diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/conf/ConfigNodeDescriptor.java b/confignode/src/main/java/org/apache/iotdb/confignode/conf/ConfigNodeDescriptor.java index 240021ae40fc..94c084851691 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/conf/ConfigNodeDescriptor.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/conf/ConfigNodeDescriptor.java @@ -91,7 +91,6 @@ else if (!urlString.endsWith(".properties")) { } private void loadProps() { - commonDescriptor.initCommonConfigDir(conf.getSystemDir()); URL url = getPropsUrl(); if (url == null) { LOGGER.warn( diff --git a/confignode/src/test/java/org/apache/iotdb/confignode/persistence/AuthorInfoTest.java b/confignode/src/test/java/org/apache/iotdb/confignode/persistence/AuthorInfoTest.java index b85987a280f4..1bf52a65b3e9 100644 --- a/confignode/src/test/java/org/apache/iotdb/confignode/persistence/AuthorInfoTest.java +++ b/confignode/src/test/java/org/apache/iotdb/confignode/persistence/AuthorInfoTest.java @@ -22,11 +22,7 @@ import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.commons.auth.AuthException; import org.apache.iotdb.commons.auth.entity.PrivilegeType; -import org.apache.iotdb.commons.conf.CommonConfig; -import org.apache.iotdb.commons.conf.CommonDescriptor; import org.apache.iotdb.commons.conf.IoTDBConstant; -import org.apache.iotdb.confignode.conf.ConfigNodeConf; -import org.apache.iotdb.confignode.conf.ConfigNodeDescriptor; import org.apache.iotdb.confignode.consensus.request.ConfigRequestType; import org.apache.iotdb.confignode.consensus.request.auth.AuthorReq; import org.apache.iotdb.confignode.consensus.response.PermissionInfoResp; @@ -54,8 +50,6 @@ public class AuthorInfoTest { private static AuthorInfo authorInfo; private static final File snapshotDir = new File(BASE_OUTPUT_PATH, "authorInfo-snapshot"); - private static final ConfigNodeConf config = ConfigNodeDescriptor.getInstance().getConf(); - private static final CommonConfig commonConfig = CommonDescriptor.getInstance().getConfig(); @BeforeClass public static void setup() { diff --git a/node-commons/src/main/java/org/apache/iotdb/commons/conf/CommonConfig.java b/node-commons/src/main/java/org/apache/iotdb/commons/conf/CommonConfig.java index a3601c1713a3..120f8e7b6368 100644 --- a/node-commons/src/main/java/org/apache/iotdb/commons/conf/CommonConfig.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/conf/CommonConfig.java @@ -42,11 +42,26 @@ public class CommonConfig { private String adminPassword = "root"; - private String userFolder = "system" + File.separator + "users"; - - private String roleFolder = "system" + File.separator + "roles"; - - private String procedureWalFolder = "system" + File.separator + "procedure"; + private String userFolder = + IoTDBConstant.DEFAULT_BASE_DIR + + File.separator + + IoTDBConstant.SYSTEM_FOLDER_NAME + + File.separator + + "users"; + + private String roleFolder = + IoTDBConstant.DEFAULT_BASE_DIR + + File.separator + + IoTDBConstant.SYSTEM_FOLDER_NAME + + File.separator + + "roles"; + + private String procedureWalFolder = + IoTDBConstant.DEFAULT_BASE_DIR + + File.separator + + IoTDBConstant.SYSTEM_FOLDER_NAME + + File.separator + + "procedure"; /** Default system file storage is in local file system (unsupported) */ private FSType systemFileStorageFs = FSType.LOCAL; diff --git a/node-commons/src/main/java/org/apache/iotdb/commons/conf/CommonDescriptor.java b/node-commons/src/main/java/org/apache/iotdb/commons/conf/CommonDescriptor.java index 523f0503ad06..da2c2c08aeaf 100644 --- a/node-commons/src/main/java/org/apache/iotdb/commons/conf/CommonDescriptor.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/conf/CommonDescriptor.java @@ -57,7 +57,6 @@ public void initCommonConfigDir(String systemDir) { } public void loadCommonProps(Properties properties) { - config.setAuthorizerProvider( properties.getProperty("authorizer_provider_class", config.getAuthorizerProvider())); // if using org.apache.iotdb.db.auth.authorizer.OpenIdAuthorizer, openID_url is needed. diff --git a/node-commons/src/main/java/org/apache/iotdb/commons/conf/IoTDBConstant.java b/node-commons/src/main/java/org/apache/iotdb/commons/conf/IoTDBConstant.java index d5c722e8dce5..1e033baf6491 100644 --- a/node-commons/src/main/java/org/apache/iotdb/commons/conf/IoTDBConstant.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/conf/IoTDBConstant.java @@ -152,6 +152,9 @@ private IoTDBConstant() {} public static final String SDT_COMP_MIN_TIME = "compmintime"; public static final String SDT_COMP_MAX_TIME = "compmaxtime"; + // default base dir, stores all IoTDB runtime files + public static final String DEFAULT_BASE_DIR = "data"; + // data folder name public static final String DATA_FOLDER_NAME = "data"; public static final String SEQUENCE_FLODER_NAME = "sequence"; diff --git a/server/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java b/server/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java index 6e9f0ce346ff..7e651bae80ae 100644 --- a/server/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java +++ b/server/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java @@ -159,7 +159,9 @@ public class IoTDBConfig { private WALMode walMode = WALMode.ASYNC; /** WAL directories */ - private String[] walDirs = {DEFAULT_BASE_DIR + File.separator + IoTDBConstant.WAL_FOLDER_NAME}; + private String[] walDirs = { + IoTDBConstant.DEFAULT_BASE_DIR + File.separator + IoTDBConstant.WAL_FOLDER_NAME + }; /** Duration a wal flush operation will wait before calling fsync. Unit: millisecond */ private volatile long fsyncWalDelayInMs = 10; @@ -216,28 +218,29 @@ public class IoTDBConfig { */ private int tlogBufferSize = 1024 * 1024; - /** default base dir, stores all IoTDB runtime files */ - private static final String DEFAULT_BASE_DIR = "data"; - /** System directory, including version file for each storage group and metadata */ - private String systemDir = DEFAULT_BASE_DIR + File.separator + IoTDBConstant.SYSTEM_FOLDER_NAME; + private String systemDir = + IoTDBConstant.DEFAULT_BASE_DIR + File.separator + IoTDBConstant.SYSTEM_FOLDER_NAME; /** Schema directory, including storage set of values. */ private String schemaDir = - DEFAULT_BASE_DIR + IoTDBConstant.DEFAULT_BASE_DIR + File.separator + IoTDBConstant.SYSTEM_FOLDER_NAME + File.separator + IoTDBConstant.SCHEMA_FOLDER_NAME; /** Sync directory, including the log and hardlink tsfiles */ - private String syncDir = DEFAULT_BASE_DIR + File.separator + IoTDBConstant.SYNC_FOLDER_NAME; + private String syncDir = + IoTDBConstant.DEFAULT_BASE_DIR + File.separator + IoTDBConstant.SYNC_FOLDER_NAME; /** Performance tracing directory, stores performance tracing files */ - private String tracingDir = DEFAULT_BASE_DIR + File.separator + IoTDBConstant.TRACING_FOLDER_NAME; + private String tracingDir = + IoTDBConstant.DEFAULT_BASE_DIR + File.separator + IoTDBConstant.TRACING_FOLDER_NAME; /** Query directory, stores temporary files of query */ - private String queryDir = DEFAULT_BASE_DIR + File.separator + IoTDBConstant.QUERY_FOLDER_NAME; + private String queryDir = + IoTDBConstant.DEFAULT_BASE_DIR + File.separator + IoTDBConstant.QUERY_FOLDER_NAME; /** External lib directory, stores user-uploaded JAR files */ private String extDir = IoTDBConstant.EXT_FOLDER_NAME; @@ -255,13 +258,15 @@ public class IoTDBConfig { IoTDBConstant.EXT_FOLDER_NAME + File.separator + IoTDBConstant.MQTT_FOLDER_NAME; /** Data directories. It can be settled as dataDirs = {"data1", "data2", "data3"}; */ - private String[] dataDirs = {DEFAULT_BASE_DIR + File.separator + IoTDBConstant.DATA_FOLDER_NAME}; + private String[] dataDirs = { + IoTDBConstant.DEFAULT_BASE_DIR + File.separator + IoTDBConstant.DATA_FOLDER_NAME + }; /** Strategy of multiple directories. */ private String multiDirStrategyClassName = null; /** Consensus directory. */ - private String consensusDir = DEFAULT_BASE_DIR + File.separator + "consensus"; + private String consensusDir = IoTDBConstant.DEFAULT_BASE_DIR + File.separator + "consensus"; /** Maximum MemTable number. Invalid when enableMemControl is true. */ private int maxMemtableNumber = 0; diff --git a/server/src/main/java/org/apache/iotdb/db/conf/IoTDBDescriptor.java b/server/src/main/java/org/apache/iotdb/db/conf/IoTDBDescriptor.java index 206484f2b8ef..f40850fef24a 100644 --- a/server/src/main/java/org/apache/iotdb/db/conf/IoTDBDescriptor.java +++ b/server/src/main/java/org/apache/iotdb/db/conf/IoTDBDescriptor.java @@ -132,7 +132,6 @@ else if (!urlString.endsWith(".properties")) { /** load an property file and set TsfileDBConfig variables. */ @SuppressWarnings("squid:S3776") // Suppress high Cognitive Complexity warning private void loadProps() { - commonDescriptor.initCommonConfigDir(conf.getSystemDir()); URL url = getPropsUrl(); if (url == null) { logger.warn("Couldn't load the configuration from any of the known sources."); From bf669f43b093805c85f3ecd28c238c5924f5f10d Mon Sep 17 00:00:00 2001 From: ZhangHongYin <46039728+SpriCoder@users.noreply.github.com> Date: Fri, 20 May 2022 12:46:56 +0800 Subject: [PATCH 057/436] [IOTDB-3202] Add cpu load and memory load into heartbeat. (#5940) --- .../thrift/impl/InternalServiceImpl.java | 59 ++++++++++++++++++- thrift-commons/src/main/thrift/common.thrift | 2 + 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/InternalServiceImpl.java b/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/InternalServiceImpl.java index 1a4661d5b4db..054e38e62268 100644 --- a/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/InternalServiceImpl.java +++ b/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/InternalServiceImpl.java @@ -59,6 +59,12 @@ import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId; import org.apache.iotdb.db.mpp.plan.planner.plan.node.write.InsertNode; import org.apache.iotdb.db.query.control.SessionManager; +import org.apache.iotdb.db.service.metrics.Metric; +import org.apache.iotdb.db.service.metrics.MetricsService; +import org.apache.iotdb.db.service.metrics.Tag; +import org.apache.iotdb.metrics.config.MetricConfigDescriptor; +import org.apache.iotdb.metrics.type.Gauge; +import org.apache.iotdb.metrics.utils.MetricLevel; import org.apache.iotdb.mpp.rpc.thrift.InternalService; import org.apache.iotdb.mpp.rpc.thrift.TCancelFragmentInstanceReq; import org.apache.iotdb.mpp.rpc.thrift.TCancelPlanFragmentReq; @@ -83,7 +89,9 @@ import org.slf4j.LoggerFactory; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.Random; import java.util.stream.Collectors; public class InternalServiceImpl implements InternalService.Iface { @@ -92,6 +100,7 @@ public class InternalServiceImpl implements InternalService.Iface { private final SchemaEngine schemaEngine = SchemaEngine.getInstance(); private final StorageEngineV2 storageEngine = StorageEngineV2.getInstance(); private final IConsensus consensusImpl = ConsensusImpl.getInstance(); + private final double loadBalanceThreshold = 0.1; public InternalServiceImpl() { super(); @@ -266,8 +275,54 @@ public TSStatus migrateDataRegion(TMigrateDataRegionReq req) throws TException { @Override public THeartbeatResp getHeartBeat(THeartbeatReq req) throws TException { - // TODO: Return load balancing messages - return new THeartbeatResp(req.getHeartbeatTimestamp()); + THeartbeatResp resp = new THeartbeatResp(req.getHeartbeatTimestamp()); + Random whetherToGetMetric = new Random(); + if (MetricConfigDescriptor.getInstance().getMetricConfig().getEnableMetric() + && whetherToGetMetric.nextDouble() < loadBalanceThreshold) { + long cpuLoad = + MetricsService.getInstance() + .getMetricManager() + .getOrCreateGauge( + Metric.SYS_CPU_LOAD.toString(), MetricLevel.CORE, Tag.NAME.toString(), "system") + .value(); + if (cpuLoad != 0) { + resp.setCpu((short) cpuLoad); + } + long usedMemory = getMemory("jvm.memory.used.bytes"); + long maxMemory = getMemory("jvm.memory.max.bytes"); + if (usedMemory != 0 && maxMemory != 0) { + resp.setMemory((short) (usedMemory * 100 / maxMemory)); + } + } + return resp; + } + + private long getMemory(String gaugeName) { + long result = 0; + try { + // + List heapIds = Arrays.asList("PS Eden Space", "PS Old Eden", "Ps Survivor Space"); + List noHeapIds = Arrays.asList("Code Cache", "Compressed Class Space", "Metaspace"); + + for (String id : heapIds) { + Gauge gauge = + MetricsService.getInstance() + .getMetricManager() + .getOrCreateGauge(gaugeName, MetricLevel.IMPORTANT, "id", id, "area", "heap"); + result += gauge.value(); + } + for (String id : noHeapIds) { + Gauge gauge = + MetricsService.getInstance() + .getMetricManager() + .getOrCreateGauge(gaugeName, MetricLevel.IMPORTANT, "id", id, "area", "noheap"); + result += gauge.value(); + } + } catch (Exception e) { + LOGGER.error("Failed to get memory from metric because {}", e.getMessage()); + return 0; + } + return result; } @Override diff --git a/thrift-commons/src/main/thrift/common.thrift b/thrift-commons/src/main/thrift/common.thrift index 9411682d1fbe..aad4d3e13a16 100644 --- a/thrift-commons/src/main/thrift/common.thrift +++ b/thrift-commons/src/main/thrift/common.thrift @@ -80,6 +80,8 @@ struct TDataNodeLocation { struct THeartbeatResp { 1: required i64 heartbeatTimestamp + 2: optional i16 cpu + 3: optional i16 memory } struct TDataNodeInfo { From 079101345de673d47217effcbf19b1c749a64d94 Mon Sep 17 00:00:00 2001 From: ZhangHongYin <46039728+SpriCoder@users.noreply.github.com> Date: Fri, 20 May 2022 13:22:13 +0800 Subject: [PATCH 058/436] fix compile. (#5967) --- .../iotdb/db/service/thrift/impl/InternalServiceImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/InternalServiceImpl.java b/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/InternalServiceImpl.java index 054e38e62268..93e679610f48 100644 --- a/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/InternalServiceImpl.java +++ b/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/InternalServiceImpl.java @@ -59,9 +59,9 @@ import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId; import org.apache.iotdb.db.mpp.plan.planner.plan.node.write.InsertNode; import org.apache.iotdb.db.query.control.SessionManager; -import org.apache.iotdb.db.service.metrics.Metric; import org.apache.iotdb.db.service.metrics.MetricsService; -import org.apache.iotdb.db.service.metrics.Tag; +import org.apache.iotdb.db.service.metrics.enums.Metric; +import org.apache.iotdb.db.service.metrics.enums.Tag; import org.apache.iotdb.metrics.config.MetricConfigDescriptor; import org.apache.iotdb.metrics.type.Gauge; import org.apache.iotdb.metrics.utils.MetricLevel; From e8d61657b4f78ae45cae8ac8be2a085257e48991 Mon Sep 17 00:00:00 2001 From: Xiangwei Wei <34242296+Alima777@users.noreply.github.com> Date: Fri, 20 May 2022 14:10:34 +0800 Subject: [PATCH 059/436] [IOTDB-3222] Implementation of AlignedSeriesAggregateScanOperator (#5938) --- .../iotdb/db/mpp/aggregation/Aggregator.java | 12 +- .../aggregation/FirstValueAccumulator.java | 3 + .../mpp/aggregation/LastValueAccumulator.java | 3 + ...Operator.java => AggregationOperator.java} | 8 +- ...r.java => RawDataAggregationOperator.java} | 10 +- .../AlignedSeriesAggregationScanOperator.java | 400 ++++++++++++ ...ava => SeriesAggregationScanOperator.java} | 26 +- .../plan/planner/LocalExecutionPlanner.java | 59 +- .../plan/planner/plan/node/PlanVisitor.java | 2 +- .../AlignedSeriesAggregationScanNode.java | 6 +- .../impl/LocalAlignedGroupByExecutor.java | 2 - ...Test.java => AggregationOperatorTest.java} | 70 +- ...gnedSeriesAggregationScanOperatorTest.java | 614 ++++++++++++++++++ ...va => RawDataAggregationOperatorTest.java} | 50 +- ...=> SeriesAggregationScanOperatorTest.java} | 146 ++--- 15 files changed, 1234 insertions(+), 177 deletions(-) rename server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/{AggregateOperator.java => AggregationOperator.java} (95%) rename server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/{RawDataAggregateOperator.java => RawDataAggregationOperator.java} (95%) create mode 100644 server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/source/AlignedSeriesAggregationScanOperator.java rename server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/source/{SeriesAggregateScanOperator.java => SeriesAggregationScanOperator.java} (96%) rename server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/{AggregateOperatorTest.java => AggregationOperatorTest.java} (87%) create mode 100644 server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/AlignedSeriesAggregationScanOperatorTest.java rename server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/{RawDataAggregateOperatorTest.java => RawDataAggregationOperatorTest.java} (90%) rename server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/{SeriesAggregateScanOperatorTest.java => SeriesAggregationScanOperatorTest.java} (80%) diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/Aggregator.java b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/Aggregator.java index 6077648e5961..b9ccc4c7b3a1 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/Aggregator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/Aggregator.java @@ -60,7 +60,6 @@ public void processTsBlock(TsBlock tsBlock) { checkArgument( step.isInputRaw(), "Step in SeriesAggregateScanOperator and RawDataAggregateOperator can only process raw input"); - // TODO Aligned TimeSeries if (inputLocationList == null) { accumulator.addInput(tsBlock.getTimeAndValueColumn(0), timeRange); } else { @@ -110,6 +109,17 @@ public void processStatistics(Statistics statistics) { accumulator.addStatistics(statistics); } + /** Used for AlignedSeriesAggregateScanOperator. */ + public void processStatistics(Statistics[] statistics) { + for (InputLocation[] inputLocations : inputLocationList) { + checkArgument( + inputLocations[0].getTsBlockIndex() == 0, + "AlignedSeriesAggregateScanOperator can only process one tsBlock input."); + int valueIndex = inputLocations[0].getValueColumnIndex(); + accumulator.addStatistics(statistics[valueIndex]); + } + } + public TSDataType[] getOutputType() { if (step.isOutputPartial()) { return accumulator.getIntermediateType(); diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/FirstValueAccumulator.java b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/FirstValueAccumulator.java index ac0f4a423fe4..f4537fbd6212 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/FirstValueAccumulator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/FirstValueAccumulator.java @@ -63,6 +63,7 @@ public void addInput(Column[] column, TimeRange timeRange) { break; case BOOLEAN: addBooleanInput(column, timeRange); + break; default: throw new UnSupportedDataTypeException( String.format("Unsupported data type in FirstValue: %s", seriesDataType)); @@ -118,8 +119,10 @@ public void addStatistics(Statistics statistics) { break; case TEXT: updateBinaryFirstValue((Binary) statistics.getFirstValue(), statistics.getStartTime()); + break; case BOOLEAN: updateBooleanFirstValue((boolean) statistics.getFirstValue(), statistics.getStartTime()); + break; default: throw new UnSupportedDataTypeException( String.format("Unsupported data type in FirstValue: %s", seriesDataType)); diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/LastValueAccumulator.java b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/LastValueAccumulator.java index b884af9d5621..bea0fb6d04cf 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/LastValueAccumulator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/LastValueAccumulator.java @@ -63,6 +63,7 @@ public void addInput(Column[] column, TimeRange timeRange) { break; case BOOLEAN: addBooleanInput(column, timeRange); + break; default: throw new UnSupportedDataTypeException( String.format("Unsupported data type in LastValue: %s", seriesDataType)); @@ -118,8 +119,10 @@ public void addStatistics(Statistics statistics) { break; case TEXT: updateBinaryLastValue((Binary) statistics.getLastValue(), statistics.getEndTime()); + break; case BOOLEAN: updateBooleanLastValue((boolean) statistics.getLastValue(), statistics.getEndTime()); + break; default: throw new UnSupportedDataTypeException( String.format("Unsupported data type in LastValue: %s", seriesDataType)); diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/AggregateOperator.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/AggregationOperator.java similarity index 95% rename from server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/AggregateOperator.java rename to server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/AggregationOperator.java index c860cd745022..23ba0d1b5708 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/AggregateOperator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/AggregationOperator.java @@ -36,14 +36,14 @@ import java.util.Arrays; import java.util.List; -import static org.apache.iotdb.db.mpp.execution.operator.source.SeriesAggregateScanOperator.initTimeRangeIterator; +import static org.apache.iotdb.db.mpp.execution.operator.source.SeriesAggregationScanOperator.initTimeRangeIterator; /** - * AggregateOperator can process the situation: aggregation of intermediate aggregate result, it + * AggregationOperator can process the situation: aggregation of intermediate aggregate result, it * will output one result based on time interval. One intermediate tsBlock input will only contain * the result of one time interval exactly. */ -public class AggregateOperator implements ProcessOperator { +public class AggregationOperator implements ProcessOperator { private final OperatorContext operatorContext; private final List aggregators; @@ -57,7 +57,7 @@ public class AggregateOperator implements ProcessOperator { // current interval of aggregation window [curStartTime, curEndTime) private TimeRange curTimeRange; - public AggregateOperator( + public AggregationOperator( OperatorContext operatorContext, List aggregators, List children, diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/RawDataAggregateOperator.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/RawDataAggregationOperator.java similarity index 95% rename from server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/RawDataAggregateOperator.java rename to server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/RawDataAggregationOperator.java index 45f376eaa2a0..fa724b12ab42 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/RawDataAggregateOperator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/RawDataAggregationOperator.java @@ -36,10 +36,10 @@ import java.util.Arrays; import java.util.List; -import static org.apache.iotdb.db.mpp.execution.operator.source.SeriesAggregateScanOperator.initTimeRangeIterator; +import static org.apache.iotdb.db.mpp.execution.operator.source.SeriesAggregationScanOperator.initTimeRangeIterator; /** - * RawDataAggregateOperator is used to process raw data tsBlock input calculating using value + * RawDataAggregationOperator is used to process raw data tsBlock input calculating using value * filter. It's possible that there is more than one tsBlock input in one time interval. And it's * also possible that one tsBlock can cover multiple time intervals too. * @@ -48,7 +48,7 @@ * *

Return aggregation result in one time interval once. */ -public class RawDataAggregateOperator implements ProcessOperator { +public class RawDataAggregationOperator implements ProcessOperator { private final OperatorContext operatorContext; private final List aggregators; @@ -63,7 +63,7 @@ public class RawDataAggregateOperator implements ProcessOperator { // Using for building result tsBlock private final TsBlockBuilder tsBlockBuilder; - public RawDataAggregateOperator( + public RawDataAggregationOperator( OperatorContext operatorContext, List aggregators, Operator child, @@ -111,7 +111,7 @@ public TsBlock next() { } // 3. Update result using aggregators - return AggregateOperator.updateResultTsBlockFromAggregators( + return AggregationOperator.updateResultTsBlockFromAggregators( tsBlockBuilder, aggregators, timeRangeIterator); } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/source/AlignedSeriesAggregationScanOperator.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/source/AlignedSeriesAggregationScanOperator.java new file mode 100644 index 000000000000..9ef84c165d1b --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/source/AlignedSeriesAggregationScanOperator.java @@ -0,0 +1,400 @@ +/* + * 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.iotdb.db.mpp.execution.operator.source; + +import org.apache.iotdb.db.engine.querycontext.QueryDataSource; +import org.apache.iotdb.db.metadata.path.AlignedPath; +import org.apache.iotdb.db.mpp.aggregation.Aggregator; +import org.apache.iotdb.db.mpp.aggregation.timerangeiterator.ITimeRangeIterator; +import org.apache.iotdb.db.mpp.execution.operator.OperatorContext; +import org.apache.iotdb.db.mpp.execution.operator.process.AggregationOperator; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId; +import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.GroupByTimeParameter; +import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; +import org.apache.iotdb.tsfile.file.metadata.statistics.Statistics; +import org.apache.iotdb.tsfile.read.common.TimeRange; +import org.apache.iotdb.tsfile.read.common.block.TsBlock; +import org.apache.iotdb.tsfile.read.common.block.TsBlock.TsBlockSingleColumnIterator; +import org.apache.iotdb.tsfile.read.common.block.TsBlockBuilder; +import org.apache.iotdb.tsfile.read.filter.basic.Filter; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; + +import static org.apache.iotdb.db.mpp.execution.operator.process.RawDataAggregationOperator.isEndCalc; +import static org.apache.iotdb.db.mpp.execution.operator.process.RawDataAggregationOperator.skipOutOfTimeRangePoints; +import static org.apache.iotdb.db.mpp.execution.operator.source.SeriesAggregationScanOperator.initTimeRangeIterator; + +/** This operator is responsible to do the aggregation calculation especially for aligned series. */ +public class AlignedSeriesAggregationScanOperator implements DataSourceOperator { + + private final OperatorContext operatorContext; + private final PlanNodeId sourceId; + private final AlignedSeriesScanUtil alignedSeriesScanUtil; + private final int subSensorSize; + private final boolean ascending; + // We still think aggregator in AlignedSeriesAggregateScanOperator is a inputRaw step. + // But in facing of statistics, it will invoke another method processStatistics() + private List aggregators; + + private ITimeRangeIterator timeRangeIterator; + // current interval of aggregation window [curStartTime, curEndTime) + private TimeRange curTimeRange; + + private TsBlock preCachedData; + + private TsBlockBuilder tsBlockBuilder; + private TsBlock resultTsBlock; + private boolean hasCachedTsBlock = false; + private boolean finished = false; + + public AlignedSeriesAggregationScanOperator( + PlanNodeId sourceId, + AlignedPath seriesPath, + OperatorContext context, + List aggregators, + Filter timeFilter, + boolean ascending, + GroupByTimeParameter groupByTimeParameter) { + this.sourceId = sourceId; + this.operatorContext = context; + this.ascending = ascending; + this.alignedSeriesScanUtil = + new AlignedSeriesScanUtil( + seriesPath, + new HashSet<>(seriesPath.getMeasurementList()), + context.getInstanceContext(), + timeFilter, + null, + ascending); + this.subSensorSize = seriesPath.getMeasurementList().size(); + this.aggregators = aggregators; + List dataTypes = new ArrayList<>(); + for (Aggregator aggregator : aggregators) { + dataTypes.addAll(Arrays.asList(aggregator.getOutputType())); + } + tsBlockBuilder = new TsBlockBuilder(dataTypes); + this.timeRangeIterator = initTimeRangeIterator(groupByTimeParameter, ascending); + } + + @Override + public OperatorContext getOperatorContext() { + return operatorContext; + } + + @Override + public TsBlock next() { + if (hasCachedTsBlock || hasNext()) { + hasCachedTsBlock = false; + return resultTsBlock; + } + return null; + } + + @Override + public boolean hasNext() { + if (hasCachedTsBlock) { + return true; + } + try { + if (!timeRangeIterator.hasNextTimeRange()) { + return false; + } + curTimeRange = timeRangeIterator.nextTimeRange(); + + // 1. Clear previous aggregation result + for (Aggregator aggregator : aggregators) { + aggregator.reset(); + aggregator.setTimeRange(curTimeRange); + } + + // 2. Calculate aggregation result based on current time window + if (calcFromCacheData(curTimeRange)) { + updateResultTsBlockFromAggregators(); + return true; + } + + // read page data firstly + if (readAndCalcFromPage(curTimeRange)) { + updateResultTsBlockFromAggregators(); + return true; + } + + // read chunk data secondly + if (readAndCalcFromChunk(curTimeRange)) { + updateResultTsBlockFromAggregators(); + return true; + } + + // read from file first + while (alignedSeriesScanUtil.hasNextFile()) { + Statistics fileTimeStatistics = alignedSeriesScanUtil.currentFileTimeStatistics(); + if (fileTimeStatistics.getStartTime() > curTimeRange.getMax()) { + if (ascending) { + updateResultTsBlockFromAggregators(); + return true; + } else { + alignedSeriesScanUtil.skipCurrentFile(); + continue; + } + } + // calc from fileMetaData + if (canUseCurrentFileStatistics() + && curTimeRange.contains( + fileTimeStatistics.getStartTime(), fileTimeStatistics.getEndTime())) { + Statistics[] statisticsList = new Statistics[subSensorSize]; + for (int i = 0; i < subSensorSize; i++) { + statisticsList[i] = alignedSeriesScanUtil.currentFileStatistics(i); + } + calcFromStatistics(statisticsList); + alignedSeriesScanUtil.skipCurrentFile(); + continue; + } + + // read chunk + if (readAndCalcFromChunk(curTimeRange)) { + updateResultTsBlockFromAggregators(); + return true; + } + } + + updateResultTsBlockFromAggregators(); + return true; + } catch (IOException e) { + throw new RuntimeException("Error while scanning the file", e); + } + } + + @Override + public boolean isFinished() { + return finished || (finished = !hasNext()); + } + + @Override + public void initQueryDataSource(QueryDataSource dataSource) { + alignedSeriesScanUtil.initQueryDataSource(dataSource); + } + + @Override + public PlanNodeId getSourceId() { + return sourceId; + } + + private void updateResultTsBlockFromAggregators() { + resultTsBlock = + AggregationOperator.updateResultTsBlockFromAggregators( + tsBlockBuilder, aggregators, timeRangeIterator); + hasCachedTsBlock = true; + } + + /** @return if already get the result */ + private boolean calcFromCacheData(TimeRange curTimeRange) throws IOException { + calcFromBatch(preCachedData, curTimeRange); + // The result is calculated from the cache + return (preCachedData != null + && (ascending + ? preCachedData.getEndTime() > curTimeRange.getMax() + : preCachedData.getStartTime() < curTimeRange.getMin())) + || isEndCalc(aggregators); + } + + @SuppressWarnings("squid:S3776") + private void calcFromBatch(TsBlock tsBlock, TimeRange curTimeRange) { + // check if the batchData does not contain points in current interval + if (tsBlock == null || !satisfied(tsBlock, curTimeRange, ascending)) { + return; + } + + // skip points that cannot be calculated + tsBlock = skipOutOfTimeRangePoints(tsBlock, curTimeRange, ascending); + + for (Aggregator aggregator : aggregators) { + // current agg method has been calculated + if (aggregator.hasFinalResult()) { + continue; + } + + aggregator.processTsBlock(tsBlock); + } + + // can calc for next interval + if (tsBlock.getTsBlockSingleColumnIterator().hasNext()) { + preCachedData = tsBlock; + } + } + + private boolean satisfied(TsBlock tsBlock, TimeRange timeRange, boolean ascending) { + TsBlockSingleColumnIterator tsBlockIterator = tsBlock.getTsBlockSingleColumnIterator(); + if (tsBlockIterator == null || !tsBlockIterator.hasNext()) { + return false; + } + + if (ascending + && (tsBlockIterator.getEndTime() < timeRange.getMin() + || tsBlockIterator.currentTime() > timeRange.getMax())) { + return false; + } + if (!ascending + && (tsBlockIterator.getEndTime() > timeRange.getMax() + || tsBlockIterator.currentTime() < timeRange.getMin())) { + preCachedData = tsBlock; + return false; + } + return true; + } + + @SuppressWarnings("squid:S3776") // Suppress high Cognitive Complexity warning + private boolean readAndCalcFromPage(TimeRange curTimeRange) throws IOException { + while (alignedSeriesScanUtil.hasNextPage()) { + Statistics pageTimeStatistics = alignedSeriesScanUtil.currentPageTimeStatistics(); + // must be non overlapped page + if (pageTimeStatistics != null) { + // There is no more eligible points in current time range + if (pageTimeStatistics.getStartTime() > curTimeRange.getMax()) { + if (ascending) { + return true; + } else { + alignedSeriesScanUtil.skipCurrentPage(); + continue; + } + } + // can use pageHeader + if (canUseCurrentPageStatistics() + && curTimeRange.contains( + pageTimeStatistics.getStartTime(), pageTimeStatistics.getEndTime())) { + Statistics[] statisticsList = new Statistics[subSensorSize]; + for (int i = 0; i < subSensorSize; i++) { + statisticsList[i] = alignedSeriesScanUtil.currentPageStatistics(i); + } + calcFromStatistics(statisticsList); + alignedSeriesScanUtil.skipCurrentPage(); + if (isEndCalc(aggregators)) { + return true; + } + continue; + } + } + + // calc from page data + TsBlock tsBlock = alignedSeriesScanUtil.nextPage(); + TsBlockSingleColumnIterator tsBlockIterator = tsBlock.getTsBlockSingleColumnIterator(); + if (tsBlockIterator == null || !tsBlockIterator.hasNext()) { + continue; + } + + // reset the last position to current Index + // lastReadIndex = tsBlockIterator.getRowIndex(); + + // stop calc and cached current batchData + if (ascending && tsBlockIterator.currentTime() > curTimeRange.getMax()) { + preCachedData = tsBlock; + return true; + } + + // calc from batch data + calcFromBatch(tsBlock, curTimeRange); + + // judge whether the calculation finished + if (isEndCalc(aggregators) + || (tsBlockIterator.hasNext() + && (ascending + ? tsBlockIterator.currentTime() > curTimeRange.getMax() + : tsBlockIterator.currentTime() < curTimeRange.getMin()))) { + return true; + } + } + return false; + } + + private boolean readAndCalcFromChunk(TimeRange curTimeRange) throws IOException { + while (alignedSeriesScanUtil.hasNextChunk()) { + Statistics chunkTimeStatistics = alignedSeriesScanUtil.currentChunkTimeStatistics(); + if (chunkTimeStatistics.getStartTime() > curTimeRange.getMax()) { + if (ascending) { + return true; + } else { + alignedSeriesScanUtil.skipCurrentChunk(); + continue; + } + } + // calc from chunkMetaData + if (canUseCurrentChunkStatistics() + && curTimeRange.contains( + chunkTimeStatistics.getStartTime(), chunkTimeStatistics.getEndTime())) { + // calc from chunkMetaData + Statistics[] statisticsList = new Statistics[subSensorSize]; + for (int i = 0; i < subSensorSize; i++) { + statisticsList[i] = alignedSeriesScanUtil.currentChunkStatistics(i); + } + calcFromStatistics(statisticsList); + alignedSeriesScanUtil.skipCurrentChunk(); + continue; + } + // read page + if (readAndCalcFromPage(curTimeRange)) { + return true; + } + } + return false; + } + + private void calcFromStatistics(Statistics[] statistics) { + for (int i = 0; i < aggregators.size(); i++) { + Aggregator aggregator = aggregators.get(i); + if (aggregator.hasFinalResult()) { + continue; + } + aggregator.processStatistics(statistics); + } + } + + public boolean canUseCurrentFileStatistics() throws IOException { + Statistics fileStatistics = alignedSeriesScanUtil.currentFileTimeStatistics(); + return !alignedSeriesScanUtil.isFileOverlapped() + && containedByTimeFilter(fileStatistics) + && !alignedSeriesScanUtil.currentFileModified(); + } + + public boolean canUseCurrentChunkStatistics() throws IOException { + Statistics chunkStatistics = alignedSeriesScanUtil.currentChunkTimeStatistics(); + return !alignedSeriesScanUtil.isChunkOverlapped() + && containedByTimeFilter(chunkStatistics) + && !alignedSeriesScanUtil.currentChunkModified(); + } + + public boolean canUseCurrentPageStatistics() throws IOException { + Statistics currentPageStatistics = alignedSeriesScanUtil.currentPageTimeStatistics(); + if (currentPageStatistics == null) { + return false; + } + return !alignedSeriesScanUtil.isPageOverlapped() + && containedByTimeFilter(currentPageStatistics) + && !alignedSeriesScanUtil.currentPageModified(); + } + + private boolean containedByTimeFilter(Statistics statistics) { + Filter timeFilter = alignedSeriesScanUtil.getTimeFilter(); + return timeFilter == null + || timeFilter.containStartEndTime(statistics.getStartTime(), statistics.getEndTime()); + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/source/SeriesAggregateScanOperator.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/source/SeriesAggregationScanOperator.java similarity index 96% rename from server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/source/SeriesAggregateScanOperator.java rename to server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/source/SeriesAggregationScanOperator.java index 92010c87ec50..c234a7c2efd6 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/source/SeriesAggregateScanOperator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/source/SeriesAggregationScanOperator.java @@ -25,7 +25,7 @@ import org.apache.iotdb.db.mpp.aggregation.timerangeiterator.SingleTimeWindowIterator; import org.apache.iotdb.db.mpp.aggregation.timerangeiterator.TimeRangeIteratorFactory; import org.apache.iotdb.db.mpp.execution.operator.OperatorContext; -import org.apache.iotdb.db.mpp.execution.operator.process.AggregateOperator; +import org.apache.iotdb.db.mpp.execution.operator.process.AggregationOperator; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId; import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.GroupByTimeParameter; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; @@ -36,16 +36,14 @@ import org.apache.iotdb.tsfile.read.common.block.TsBlockBuilder; import org.apache.iotdb.tsfile.read.filter.basic.Filter; -import com.google.common.util.concurrent.ListenableFuture; - import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Set; -import static org.apache.iotdb.db.mpp.execution.operator.process.RawDataAggregateOperator.isEndCalc; -import static org.apache.iotdb.db.mpp.execution.operator.process.RawDataAggregateOperator.skipOutOfTimeRangePoints; +import static org.apache.iotdb.db.mpp.execution.operator.process.RawDataAggregationOperator.isEndCalc; +import static org.apache.iotdb.db.mpp.execution.operator.process.RawDataAggregationOperator.skipOutOfTimeRangePoints; /** * This operator is responsible to do the aggregation calculation for one series based on global @@ -55,7 +53,7 @@ * In sliding window situation, current time window is a pre-aggregation window. If there is no time * split parameter, i.e. aggregation without groupBy, just one tsBlock will be returned. */ -public class SeriesAggregateScanOperator implements DataSourceOperator { +public class SeriesAggregationScanOperator implements DataSourceOperator { private final OperatorContext operatorContext; private final PlanNodeId sourceId; @@ -76,7 +74,7 @@ public class SeriesAggregateScanOperator implements DataSourceOperator { private boolean hasCachedTsBlock = false; private boolean finished = false; - public SeriesAggregateScanOperator( + public SeriesAggregationScanOperator( PlanNodeId sourceId, PartialPath seriesPath, Set allSensors, @@ -134,12 +132,6 @@ public OperatorContext getOperatorContext() { return operatorContext; } - // TODO - @Override - public ListenableFuture isBlocked() { - return DataSourceOperator.super.isBlocked(); - } - @Override public TsBlock next() { if (hasCachedTsBlock || hasNext()) { @@ -220,17 +212,11 @@ public boolean hasNext() { private void updateResultTsBlockFromAggregators() { resultTsBlock = - AggregateOperator.updateResultTsBlockFromAggregators( + AggregationOperator.updateResultTsBlockFromAggregators( tsBlockBuilder, aggregators, timeRangeIterator); hasCachedTsBlock = true; } - // TODO Implement it later? - @Override - public void close() throws Exception { - DataSourceOperator.super.close(); - } - @Override public boolean isFinished() { return finished || (finished = !hasNext()); diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LocalExecutionPlanner.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LocalExecutionPlanner.java index 689745cb9180..a6d1d16375fb 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LocalExecutionPlanner.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LocalExecutionPlanner.java @@ -37,7 +37,7 @@ import org.apache.iotdb.db.mpp.execution.fragment.FragmentInstanceContext; import org.apache.iotdb.db.mpp.execution.operator.Operator; import org.apache.iotdb.db.mpp.execution.operator.OperatorContext; -import org.apache.iotdb.db.mpp.execution.operator.process.AggregateOperator; +import org.apache.iotdb.db.mpp.execution.operator.process.AggregationOperator; import org.apache.iotdb.db.mpp.execution.operator.process.DeviceMergeOperator; import org.apache.iotdb.db.mpp.execution.operator.process.DeviceViewOperator; import org.apache.iotdb.db.mpp.execution.operator.process.FillOperator; @@ -46,7 +46,7 @@ import org.apache.iotdb.db.mpp.execution.operator.process.LinearFillOperator; import org.apache.iotdb.db.mpp.execution.operator.process.OffsetOperator; import org.apache.iotdb.db.mpp.execution.operator.process.ProcessOperator; -import org.apache.iotdb.db.mpp.execution.operator.process.RawDataAggregateOperator; +import org.apache.iotdb.db.mpp.execution.operator.process.RawDataAggregationOperator; import org.apache.iotdb.db.mpp.execution.operator.process.TimeJoinOperator; import org.apache.iotdb.db.mpp.execution.operator.process.TransformOperator; import org.apache.iotdb.db.mpp.execution.operator.process.fill.IFill; @@ -86,10 +86,11 @@ import org.apache.iotdb.db.mpp.execution.operator.schema.SchemaQueryMergeOperator; import org.apache.iotdb.db.mpp.execution.operator.schema.TimeSeriesCountOperator; import org.apache.iotdb.db.mpp.execution.operator.schema.TimeSeriesSchemaScanOperator; +import org.apache.iotdb.db.mpp.execution.operator.source.AlignedSeriesAggregationScanOperator; import org.apache.iotdb.db.mpp.execution.operator.source.AlignedSeriesScanOperator; import org.apache.iotdb.db.mpp.execution.operator.source.DataSourceOperator; import org.apache.iotdb.db.mpp.execution.operator.source.ExchangeOperator; -import org.apache.iotdb.db.mpp.execution.operator.source.SeriesAggregateScanOperator; +import org.apache.iotdb.db.mpp.execution.operator.source.SeriesAggregationScanOperator; import org.apache.iotdb.db.mpp.execution.operator.source.SeriesScanOperator; import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNode; @@ -121,6 +122,7 @@ import org.apache.iotdb.db.mpp.plan.planner.plan.node.process.TimeJoinNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.process.TransformNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.sink.FragmentSinkNode; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.source.AlignedSeriesAggregationScanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.source.AlignedSeriesScanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.source.SeriesAggregationScanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.source.SeriesScanNode; @@ -139,6 +141,7 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; @@ -264,6 +267,48 @@ public Operator visitAlignedSeriesScan( return seriesScanOperator; } + @Override + public Operator visitAlignedSeriesAggregationScan( + AlignedSeriesAggregationScanNode node, LocalExecutionPlanContext context) { + AlignedPath seriesPath = node.getAlignedPath(); + boolean ascending = node.getScanOrder() == OrderBy.TIMESTAMP_ASC; + OperatorContext operatorContext = + context.instanceContext.addOperatorContext( + context.getNextOperatorId(), + node.getPlanNodeId(), + AlignedSeriesAggregationScanOperator.class.getSimpleName()); + List aggregators = new ArrayList<>(); + for (AggregationDescriptor descriptor : node.getAggregationDescriptorList()) { + // I am not sure that it's correct or not + String inputSeries = descriptor.getParametersString(); + int seriesIndex = seriesPath.getMeasurementList().indexOf(inputSeries); + TSDataType seriesDataType = + seriesPath.getMeasurementSchema().getSubMeasurementsTSDataTypeList().get(seriesIndex); + aggregators.add( + new Aggregator( + AccumulatorFactory.createAccumulator( + descriptor.getAggregationType(), seriesDataType, ascending), + descriptor.getStep(), + Collections.singletonList( + new InputLocation[] {new InputLocation(0, seriesIndex)}))); + } + + AlignedSeriesAggregationScanOperator seriesAggregationScanOperator = + new AlignedSeriesAggregationScanOperator( + node.getPlanNodeId(), + seriesPath, + operatorContext, + aggregators, + node.getTimeFilter(), + ascending, + node.getGroupByTimeParameter()); + + context.addSourceOperator(seriesAggregationScanOperator); + context.addPath(seriesPath); + + return seriesAggregationScanOperator; + } + @Override public Operator visitSchemaQueryScan( SchemaQueryScanNode node, LocalExecutionPlanContext context) { @@ -452,8 +497,8 @@ public Operator visitSeriesAggregationScan( node.getSeriesPath().getSeriesType(), ascending), o.getStep()))); - SeriesAggregateScanOperator aggregateScanOperator = - new SeriesAggregateScanOperator( + SeriesAggregationScanOperator aggregateScanOperator = + new SeriesAggregationScanOperator( node.getPlanNodeId(), seriesPath, context.getAllSensors(seriesPath.getDevice(), seriesPath.getMeasurement()), @@ -784,14 +829,14 @@ public Operator visitRowBasedSeriesAggregate( boolean inputRaw = node.getAggregationDescriptorList().get(0).getStep().isInputRaw(); if (inputRaw) { checkArgument(children.size() == 1, "rawDataAggregateOperator can only accept one input"); - return new RawDataAggregateOperator( + return new RawDataAggregationOperator( operatorContext, aggregators, children.get(0), ascending, node.getGroupByTimeParameter()); } else { - return new AggregateOperator( + return new AggregationOperator( operatorContext, aggregators, children, ascending, node.getGroupByTimeParameter()); } } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/PlanVisitor.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/PlanVisitor.java index 2cab8c04d4aa..53132f3d0c84 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/PlanVisitor.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/PlanVisitor.java @@ -81,7 +81,7 @@ public R visitAlignedSeriesScan(AlignedSeriesScanNode node, C context) { return visitPlan(node, context); } - public R visitAlignedSeriesAggregate(AlignedSeriesAggregationScanNode node, C context) { + public R visitAlignedSeriesAggregationScan(AlignedSeriesAggregationScanNode node, C context) { return visitPlan(node, context); } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/source/AlignedSeriesAggregationScanNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/source/AlignedSeriesAggregationScanNode.java index c57e6692b5fc..0345031d076d 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/source/AlignedSeriesAggregationScanNode.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/source/AlignedSeriesAggregationScanNode.java @@ -27,6 +27,7 @@ import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeType; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanVisitor; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.process.AggregationNode; import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.AggregationDescriptor; import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.GroupByTimeParameter; import org.apache.iotdb.db.mpp.plan.statement.component.OrderBy; @@ -74,7 +75,8 @@ public AlignedSeriesAggregationScanNode( List aggregationDescriptorList) { super(id); this.alignedPath = alignedPath; - this.aggregationDescriptorList = aggregationDescriptorList; + this.aggregationDescriptorList = + AggregationNode.getDeduplicatedDescriptors(aggregationDescriptorList); } public AlignedSeriesAggregationScanNode( @@ -181,7 +183,7 @@ public List getOutputColumnNames() { @Override public R accept(PlanVisitor visitor, C context) { - return visitor.visitAlignedSeriesAggregate(this, context); + return visitor.visitAlignedSeriesAggregationScan(this, context); } @Override diff --git a/server/src/main/java/org/apache/iotdb/db/query/executor/groupby/impl/LocalAlignedGroupByExecutor.java b/server/src/main/java/org/apache/iotdb/db/query/executor/groupby/impl/LocalAlignedGroupByExecutor.java index 9a47eaad0a0e..aa5ede9abdb8 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/executor/groupby/impl/LocalAlignedGroupByExecutor.java +++ b/server/src/main/java/org/apache/iotdb/db/query/executor/groupby/impl/LocalAlignedGroupByExecutor.java @@ -246,9 +246,7 @@ private boolean readAndCalcFromPage(long curStartTime, long curEndTime) // set initial Index lastReadCurArrayIndex = batchData.getReadCurArrayIndex(); - ; lastReadCurListIndex = batchData.getReadCurListIndex(); - ; // stop calc and cached current batchData if (ascending && batchData.currentTime() >= curEndTime) { diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/AggregateOperatorTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/AggregationOperatorTest.java similarity index 87% rename from server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/AggregateOperatorTest.java rename to server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/AggregationOperatorTest.java index d4a3392e97ca..eaf58a6f221b 100644 --- a/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/AggregateOperatorTest.java +++ b/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/AggregationOperatorTest.java @@ -33,8 +33,8 @@ import org.apache.iotdb.db.mpp.common.QueryId; import org.apache.iotdb.db.mpp.execution.fragment.FragmentInstanceContext; import org.apache.iotdb.db.mpp.execution.fragment.FragmentInstanceStateMachine; -import org.apache.iotdb.db.mpp.execution.operator.process.AggregateOperator; -import org.apache.iotdb.db.mpp.execution.operator.source.SeriesAggregateScanOperator; +import org.apache.iotdb.db.mpp.execution.operator.process.AggregationOperator; +import org.apache.iotdb.db.mpp.execution.operator.source.SeriesAggregationScanOperator; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId; import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.AggregationStep; import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.GroupByTimeParameter; @@ -59,9 +59,9 @@ import static org.apache.iotdb.db.mpp.execution.fragment.FragmentInstanceContext.createFragmentInstanceContext; import static org.junit.Assert.assertEquals; -public class AggregateOperatorTest { +public class AggregationOperatorTest { - private static final String AGGREGATE_OPERATOR_TEST_SG = "root.AggregateOperatorTest"; + private static final String AGGREGATION_OPERATOR_TEST_SG = "root.AggregationOperatorTest"; private final List deviceIds = new ArrayList<>(); private final List measurementSchemas = new ArrayList<>(); @@ -73,7 +73,7 @@ public class AggregateOperatorTest { @Before public void setUp() throws MetadataException, IOException, WriteProcessException { SeriesReaderTestUtil.setUp( - measurementSchemas, deviceIds, seqResources, unSeqResources, AGGREGATE_OPERATOR_TEST_SG); + measurementSchemas, deviceIds, seqResources, unSeqResources, AGGREGATION_OPERATOR_TEST_SG); this.instanceNotificationExecutor = IoTDBThreadPoolFactory.newFixedThreadPool(1, "test-instance-notification"); } @@ -101,11 +101,11 @@ public void testAggregateIntermediateResult1() throws IllegalPathException { inputLocationForOneAggregator.add(new InputLocation[] {new InputLocation(1, i)}); inputLocations.add(inputLocationForOneAggregator); } - AggregateOperator aggregateOperator = - initAggregateOperator(aggregationTypes, null, inputLocations); + AggregationOperator aggregationOperator = + initAggregationOperator(aggregationTypes, null, inputLocations); int count = 0; - while (aggregateOperator.hasNext()) { - TsBlock resultTsBlock = aggregateOperator.next(); + while (aggregationOperator.hasNext()) { + TsBlock resultTsBlock = aggregationOperator.next(); assertEquals(500, resultTsBlock.getColumn(0).getLong(0)); assertEquals(6524750.0, resultTsBlock.getColumn(1).getDouble(0), 0.0001); assertEquals(0, resultTsBlock.getColumn(2).getLong(0)); @@ -133,11 +133,11 @@ public void testAggregateIntermediateResult2() throws IllegalPathException { new InputLocation[] {new InputLocation(1, 2 * i), new InputLocation(1, 2 * i + 1)}); inputLocations.add(inputLocationForOneAggregator); } - AggregateOperator aggregateOperator = - initAggregateOperator(aggregationTypes, null, inputLocations); + AggregationOperator aggregationOperator = + initAggregationOperator(aggregationTypes, null, inputLocations); int count = 0; - while (aggregateOperator.hasNext()) { - TsBlock resultTsBlock = aggregateOperator.next(); + while (aggregationOperator.hasNext()) { + TsBlock resultTsBlock = aggregationOperator.next(); assertEquals(13049.5, resultTsBlock.getColumn(0).getDouble(0), 0.001); assertEquals(20000, resultTsBlock.getColumn(1).getInt(0)); assertEquals(10499, resultTsBlock.getColumn(2).getInt(0)); @@ -172,11 +172,11 @@ public void testGroupByIntermediateResult1() throws IllegalPathException { inputLocationForOneAggregator.add(new InputLocation[] {new InputLocation(1, i)}); inputLocations.add(inputLocationForOneAggregator); } - AggregateOperator aggregateOperator = - initAggregateOperator(aggregationTypes, groupByTimeParameter, inputLocations); + AggregationOperator aggregationOperator = + initAggregationOperator(aggregationTypes, groupByTimeParameter, inputLocations); int count = 0; - while (aggregateOperator.hasNext()) { - TsBlock resultTsBlock = aggregateOperator.next(); + while (aggregationOperator.hasNext()) { + TsBlock resultTsBlock = aggregationOperator.next(); assertEquals(100 * count, resultTsBlock.getTimeColumn().getLong(0)); assertEquals(result[0][count], resultTsBlock.getColumn(0).getLong(0)); assertEquals(result[1][count], resultTsBlock.getColumn(1).getDouble(0), 0.0001); @@ -211,11 +211,11 @@ public void testGroupByIntermediateResult2() throws IllegalPathException { new InputLocation[] {new InputLocation(1, 2 * i), new InputLocation(1, 2 * i + 1)}); inputLocations.add(inputLocationForOneAggregator); } - AggregateOperator aggregateOperator = - initAggregateOperator(aggregationTypes, groupByTimeParameter, inputLocations); + AggregationOperator aggregationOperator = + initAggregationOperator(aggregationTypes, groupByTimeParameter, inputLocations); int count = 0; - while (aggregateOperator.hasNext()) { - TsBlock resultTsBlock = aggregateOperator.next(); + while (aggregationOperator.hasNext()) { + TsBlock resultTsBlock = aggregationOperator.next(); assertEquals(100 * count, resultTsBlock.getTimeColumn().getLong(0)); assertEquals(result[0][count], resultTsBlock.getColumn(0).getDouble(0), 0.001); assertEquals((int) result[1][count], resultTsBlock.getColumn(1).getInt(0)); @@ -230,7 +230,7 @@ public void testGroupByIntermediateResult2() throws IllegalPathException { * @param groupByTimeParameter group by time parameter * @param inputLocations each inputLocation is used in one aggregator */ - private AggregateOperator initAggregateOperator( + private AggregationOperator initAggregationOperator( List aggregationTypes, GroupByTimeParameter groupByTimeParameter, List> inputLocations) @@ -245,21 +245,21 @@ private AggregateOperator initAggregateOperator( createFragmentInstanceContext(instanceId, stateMachine); PlanNodeId planNodeId1 = new PlanNodeId("1"); fragmentInstanceContext.addOperatorContext( - 1, planNodeId1, SeriesAggregateScanOperator.class.getSimpleName()); + 1, planNodeId1, SeriesAggregationScanOperator.class.getSimpleName()); PlanNodeId planNodeId2 = new PlanNodeId("2"); fragmentInstanceContext.addOperatorContext( - 2, planNodeId2, SeriesAggregateScanOperator.class.getSimpleName()); + 2, planNodeId2, SeriesAggregationScanOperator.class.getSimpleName()); PlanNodeId planNodeId3 = new PlanNodeId("3"); fragmentInstanceContext.addOperatorContext( - 3, planNodeId3, AggregateOperator.class.getSimpleName()); + 3, planNodeId3, AggregationOperator.class.getSimpleName()); MeasurementPath measurementPath1 = - new MeasurementPath(AGGREGATE_OPERATOR_TEST_SG + ".device0.sensor0", TSDataType.INT32); + new MeasurementPath(AGGREGATION_OPERATOR_TEST_SG + ".device0.sensor0", TSDataType.INT32); List aggregators = new ArrayList<>(); AccumulatorFactory.createAccumulators(aggregationTypes, TSDataType.INT32, true) .forEach(o -> aggregators.add(new Aggregator(o, AggregationStep.PARTIAL))); - SeriesAggregateScanOperator seriesAggregateScanOperator1 = - new SeriesAggregateScanOperator( + SeriesAggregationScanOperator seriesAggregationScanOperator1 = + new SeriesAggregationScanOperator( planNodeId1, measurementPath1, Collections.singleton("sensor0"), @@ -277,11 +277,11 @@ private AggregateOperator initAggregateOperator( unSeqResources1.add(unSeqResources.get(1)); unSeqResources1.add(unSeqResources.get(3)); unSeqResources1.add(unSeqResources.get(5)); - seriesAggregateScanOperator1.initQueryDataSource( + seriesAggregationScanOperator1.initQueryDataSource( new QueryDataSource(seqResources1, unSeqResources1)); - SeriesAggregateScanOperator seriesAggregateScanOperator2 = - new SeriesAggregateScanOperator( + SeriesAggregationScanOperator seriesAggregationScanOperator2 = + new SeriesAggregationScanOperator( planNodeId2, measurementPath1, Collections.singleton("sensor0"), @@ -296,12 +296,12 @@ private AggregateOperator initAggregateOperator( seqResources2.add(seqResources.get(4)); unSeqResources2.add(unSeqResources.get(2)); unSeqResources2.add(unSeqResources.get(4)); - seriesAggregateScanOperator2.initQueryDataSource( + seriesAggregationScanOperator2.initQueryDataSource( new QueryDataSource(seqResources2, unSeqResources2)); List children = new ArrayList<>(); - children.add(seriesAggregateScanOperator1); - children.add(seriesAggregateScanOperator2); + children.add(seriesAggregationScanOperator1); + children.add(seriesAggregationScanOperator2); List finalAggregators = new ArrayList<>(); List accumulators = @@ -311,7 +311,7 @@ private AggregateOperator initAggregateOperator( new Aggregator(accumulators.get(i), AggregationStep.FINAL, inputLocations.get(i))); } - return new AggregateOperator( + return new AggregationOperator( fragmentInstanceContext.getOperatorContexts().get(2), finalAggregators, children, diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/AlignedSeriesAggregationScanOperatorTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/AlignedSeriesAggregationScanOperatorTest.java new file mode 100644 index 000000000000..fbd76ab819f2 --- /dev/null +++ b/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/AlignedSeriesAggregationScanOperatorTest.java @@ -0,0 +1,614 @@ +/* + * 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.iotdb.db.mpp.execution.operator; + +import org.apache.iotdb.commons.concurrent.IoTDBThreadPoolFactory; +import org.apache.iotdb.commons.exception.IllegalPathException; +import org.apache.iotdb.commons.exception.MetadataException; +import org.apache.iotdb.db.engine.querycontext.QueryDataSource; +import org.apache.iotdb.db.engine.storagegroup.TsFileResource; +import org.apache.iotdb.db.metadata.path.AlignedPath; +import org.apache.iotdb.db.mpp.aggregation.AccumulatorFactory; +import org.apache.iotdb.db.mpp.aggregation.Aggregator; +import org.apache.iotdb.db.mpp.common.FragmentInstanceId; +import org.apache.iotdb.db.mpp.common.PlanFragmentId; +import org.apache.iotdb.db.mpp.common.QueryId; +import org.apache.iotdb.db.mpp.execution.fragment.FragmentInstanceContext; +import org.apache.iotdb.db.mpp.execution.fragment.FragmentInstanceStateMachine; +import org.apache.iotdb.db.mpp.execution.operator.source.AlignedSeriesAggregationScanOperator; +import org.apache.iotdb.db.mpp.execution.operator.source.SeriesScanOperator; +import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId; +import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.AggregationStep; +import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.GroupByTimeParameter; +import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.InputLocation; +import org.apache.iotdb.db.query.aggregation.AggregationType; +import org.apache.iotdb.tsfile.exception.write.WriteProcessException; +import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; +import org.apache.iotdb.tsfile.read.common.block.TsBlock; +import org.apache.iotdb.tsfile.read.filter.TimeFilter; +import org.apache.iotdb.tsfile.read.filter.basic.Filter; +import org.apache.iotdb.tsfile.read.filter.operator.AndFilter; +import org.apache.iotdb.tsfile.write.schema.IMeasurementSchema; +import org.apache.iotdb.tsfile.write.schema.MeasurementSchema; + +import com.google.common.collect.Sets; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.stream.Collectors; + +import static org.apache.iotdb.db.mpp.execution.fragment.FragmentInstanceContext.createFragmentInstanceContext; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class AlignedSeriesAggregationScanOperatorTest { + + private static final String SERIES_AGGREGATION_SCAN_OPERATOR_TEST_SG = + "root.AlignedSeriesAggregationScanOperatorTest"; + private static final List measurementSchemas = new ArrayList<>(); + + private static final List seqResources = new ArrayList<>(); + private static final List unSeqResources = new ArrayList<>(); + + private ExecutorService instanceNotificationExecutor = + IoTDBThreadPoolFactory.newFixedThreadPool(1, "test-instance-notification");; + private static final double DELTA = 0.000001; + + @BeforeClass + public static void setUp() throws MetadataException, IOException, WriteProcessException { + AlignedSeriesTestUtil.setUp( + measurementSchemas, seqResources, unSeqResources, SERIES_AGGREGATION_SCAN_OPERATOR_TEST_SG); + } + + @AfterClass + public static void tearDown() throws IOException { + AlignedSeriesTestUtil.tearDown(seqResources, unSeqResources); + } + + @Test + public void testAggregationWithoutTimeFilter() throws IllegalPathException { + List aggregators = new ArrayList<>(); + for (int i = 0; i < measurementSchemas.size(); i++) { + TSDataType dataType = measurementSchemas.get(i).getType(); + List inputLocations = new ArrayList<>(); + inputLocations.add(new InputLocation[] {new InputLocation(0, i)}); + aggregators.add( + new Aggregator( + AccumulatorFactory.createAccumulator(AggregationType.COUNT, dataType, true), + AggregationStep.SINGLE, + inputLocations)); + } + AlignedSeriesAggregationScanOperator seriesAggregationScanOperator = + initAlignedSeriesAggregationScanOperator(aggregators, null, true, null); + int count = 0; + while (seriesAggregationScanOperator.hasNext()) { + TsBlock resultTsBlock = seriesAggregationScanOperator.next(); + for (int i = 0; i < measurementSchemas.size(); i++) { + assertEquals(500, resultTsBlock.getColumn(i).getLong(0)); + } + count++; + } + assertEquals(1, count); + } + + @Test + public void testAggregationWithoutTimeFilterOrderByTimeDesc() throws IllegalPathException { + List aggregators = new ArrayList<>(); + for (int i = 0; i < measurementSchemas.size(); i++) { + TSDataType dataType = measurementSchemas.get(i).getType(); + List inputLocations = new ArrayList<>(); + inputLocations.add(new InputLocation[] {new InputLocation(0, i)}); + aggregators.add( + new Aggregator( + AccumulatorFactory.createAccumulator(AggregationType.COUNT, dataType, false), + AggregationStep.SINGLE, + inputLocations)); + } + AlignedSeriesAggregationScanOperator seriesAggregationScanOperator = + initAlignedSeriesAggregationScanOperator(aggregators, null, false, null); + int count = 0; + while (seriesAggregationScanOperator.hasNext()) { + TsBlock resultTsBlock = seriesAggregationScanOperator.next(); + for (int i = 0; i < measurementSchemas.size(); i++) { + assertEquals(500, resultTsBlock.getColumn(i).getLong(0)); + } + count++; + } + assertEquals(1, count); + } + + @Test + public void testMultiAggregationFuncWithoutTimeFilter1() throws IllegalPathException { + List aggregationTypes = new ArrayList<>(); + aggregationTypes.add(AggregationType.COUNT); + aggregationTypes.add(AggregationType.SUM); + List aggregators = new ArrayList<>(); + for (int i = 0; i < 2; i++) { + TSDataType dataType = measurementSchemas.get(i).getType(); + List inputLocations = new ArrayList<>(); + inputLocations.add(new InputLocation[] {new InputLocation(0, i)}); + aggregators.add( + new Aggregator( + AccumulatorFactory.createAccumulator(aggregationTypes.get(i), dataType, true), + AggregationStep.SINGLE, + inputLocations)); + } + AlignedSeriesAggregationScanOperator seriesAggregationScanOperator = + initAlignedSeriesAggregationScanOperator(aggregators, null, true, null); + int count = 0; + while (seriesAggregationScanOperator.hasNext()) { + TsBlock resultTsBlock = seriesAggregationScanOperator.next(); + assertEquals(500, resultTsBlock.getColumn(0).getLong(0)); + assertEquals(6524750.0, resultTsBlock.getColumn(1).getDouble(0), 0.0001); + count++; + } + assertEquals(1, count); + } + + @Test + public void testMultiAggregationFuncWithoutTimeFilter2() throws IllegalPathException { + List aggregationTypes = new ArrayList<>(); + aggregationTypes.add(AggregationType.FIRST_VALUE); + aggregationTypes.add(AggregationType.LAST_VALUE); + aggregationTypes.add(AggregationType.MAX_VALUE); + aggregationTypes.add(AggregationType.MIN_VALUE); + aggregationTypes.add(AggregationType.MIN_TIME); + aggregationTypes.add(AggregationType.MAX_TIME); + List aggregators = new ArrayList<>(); + for (int i = 0; i < 6; i++) { + TSDataType dataType = measurementSchemas.get(i).getType(); + List inputLocations = new ArrayList<>(); + inputLocations.add(new InputLocation[] {new InputLocation(0, i)}); + aggregators.add( + new Aggregator( + AccumulatorFactory.createAccumulator(aggregationTypes.get(i), dataType, true), + AggregationStep.SINGLE, + inputLocations)); + } + AlignedSeriesAggregationScanOperator seriesAggregationScanOperator = + initAlignedSeriesAggregationScanOperator(aggregators, null, true, null); + int count = 0; + while (seriesAggregationScanOperator.hasNext()) { + TsBlock resultTsBlock = seriesAggregationScanOperator.next(); + assertTrue(resultTsBlock.getColumn(0).getBoolean(0)); + assertEquals(10499, resultTsBlock.getColumn(1).getInt(0)); + assertEquals(20199, resultTsBlock.getColumn(2).getLong(0)); + assertEquals(260.0, resultTsBlock.getColumn(3).getFloat(0), DELTA); + assertEquals(0, resultTsBlock.getColumn(4).getLong(0)); + assertEquals(499, resultTsBlock.getColumn(5).getLong(0)); + count++; + } + assertEquals(1, count); + } + + @Test + public void testMultiAggregationFuncWithoutTimeFilterOrderByTimeDesc() + throws IllegalPathException { + List aggregationTypes = new ArrayList<>(); + aggregationTypes.add(AggregationType.FIRST_VALUE); + aggregationTypes.add(AggregationType.LAST_VALUE); + aggregationTypes.add(AggregationType.MAX_VALUE); + aggregationTypes.add(AggregationType.MIN_VALUE); + aggregationTypes.add(AggregationType.MIN_TIME); + aggregationTypes.add(AggregationType.MAX_TIME); + List aggregators = new ArrayList<>(); + for (int i = 0; i < 6; i++) { + TSDataType dataType = measurementSchemas.get(i).getType(); + List inputLocations = new ArrayList<>(); + inputLocations.add(new InputLocation[] {new InputLocation(0, i)}); + aggregators.add( + new Aggregator( + AccumulatorFactory.createAccumulator(aggregationTypes.get(i), dataType, false), + AggregationStep.SINGLE, + inputLocations)); + } + AlignedSeriesAggregationScanOperator seriesAggregationScanOperator = + initAlignedSeriesAggregationScanOperator(aggregators, null, false, null); + int count = 0; + while (seriesAggregationScanOperator.hasNext()) { + TsBlock resultTsBlock = seriesAggregationScanOperator.next(); + assertTrue(resultTsBlock.getColumn(0).getBoolean(0)); + assertEquals(10499, resultTsBlock.getColumn(1).getInt(0)); + assertEquals(20199, resultTsBlock.getColumn(2).getLong(0)); + assertEquals(260.0, resultTsBlock.getColumn(3).getFloat(0), DELTA); + assertEquals(0, resultTsBlock.getColumn(4).getLong(0)); + assertEquals(499, resultTsBlock.getColumn(5).getLong(0)); + count++; + } + assertEquals(1, count); + } + + @Test + public void testAggregationWithTimeFilter1() throws IllegalPathException { + List aggregators = new ArrayList<>(); + for (int i = 0; i < measurementSchemas.size(); i++) { + TSDataType dataType = measurementSchemas.get(i).getType(); + List inputLocations = new ArrayList<>(); + inputLocations.add(new InputLocation[] {new InputLocation(0, i)}); + aggregators.add( + new Aggregator( + AccumulatorFactory.createAccumulator(AggregationType.COUNT, dataType, true), + AggregationStep.SINGLE, + inputLocations)); + } + Filter timeFilter = TimeFilter.gtEq(120); + AlignedSeriesAggregationScanOperator seriesAggregationScanOperator = + initAlignedSeriesAggregationScanOperator(aggregators, timeFilter, true, null); + int count = 0; + while (seriesAggregationScanOperator.hasNext()) { + TsBlock resultTsBlock = seriesAggregationScanOperator.next(); + for (int i = 0; i < measurementSchemas.size(); i++) { + assertEquals(resultTsBlock.getColumn(i).getLong(0), 380); + } + count++; + } + assertEquals(1, count); + } + + @Test + public void testAggregationWithTimeFilter2() throws IllegalPathException { + Filter timeFilter = TimeFilter.ltEq(379); + List aggregators = new ArrayList<>(); + for (int i = 0; i < measurementSchemas.size(); i++) { + TSDataType dataType = measurementSchemas.get(i).getType(); + List inputLocations = new ArrayList<>(); + inputLocations.add(new InputLocation[] {new InputLocation(0, i)}); + aggregators.add( + new Aggregator( + AccumulatorFactory.createAccumulator(AggregationType.COUNT, dataType, true), + AggregationStep.SINGLE, + inputLocations)); + } + AlignedSeriesAggregationScanOperator seriesAggregationScanOperator = + initAlignedSeriesAggregationScanOperator(aggregators, timeFilter, true, null); + int count = 0; + while (seriesAggregationScanOperator.hasNext()) { + TsBlock resultTsBlock = seriesAggregationScanOperator.next(); + for (int i = 0; i < measurementSchemas.size(); i++) { + assertEquals(resultTsBlock.getColumn(i).getLong(0), 380); + } + count++; + } + assertEquals(1, count); + } + + @Test + public void testAggregationWithTimeFilter3() throws IllegalPathException { + Filter timeFilter = new AndFilter(TimeFilter.gtEq(100), TimeFilter.ltEq(399)); + List aggregators = new ArrayList<>(); + for (int i = 0; i < measurementSchemas.size(); i++) { + TSDataType dataType = measurementSchemas.get(i).getType(); + List inputLocations = new ArrayList<>(); + inputLocations.add(new InputLocation[] {new InputLocation(0, i)}); + aggregators.add( + new Aggregator( + AccumulatorFactory.createAccumulator(AggregationType.COUNT, dataType, true), + AggregationStep.SINGLE, + inputLocations)); + } + AlignedSeriesAggregationScanOperator seriesAggregationScanOperator = + initAlignedSeriesAggregationScanOperator(aggregators, timeFilter, true, null); + int count = 0; + while (seriesAggregationScanOperator.hasNext()) { + TsBlock resultTsBlock = seriesAggregationScanOperator.next(); + for (int i = 0; i < measurementSchemas.size(); i++) { + assertEquals(resultTsBlock.getColumn(i).getLong(0), 300); + } + count++; + } + assertEquals(1, count); + } + + @Test + public void testMultiAggregationWithTimeFilter() throws IllegalPathException { + List aggregationTypes = new ArrayList<>(); + aggregationTypes.add(AggregationType.FIRST_VALUE); + aggregationTypes.add(AggregationType.LAST_VALUE); + aggregationTypes.add(AggregationType.MAX_VALUE); + aggregationTypes.add(AggregationType.MIN_VALUE); + aggregationTypes.add(AggregationType.MIN_TIME); + aggregationTypes.add(AggregationType.MAX_TIME); + List aggregators = new ArrayList<>(); + for (int i = 0; i < 6; i++) { + TSDataType dataType = measurementSchemas.get(i).getType(); + List inputLocations = new ArrayList<>(); + inputLocations.add(new InputLocation[] {new InputLocation(0, i)}); + aggregators.add( + new Aggregator( + AccumulatorFactory.createAccumulator(aggregationTypes.get(i), dataType, true), + AggregationStep.SINGLE, + inputLocations)); + } + Filter timeFilter = new AndFilter(TimeFilter.gtEq(100), TimeFilter.ltEq(399)); + AlignedSeriesAggregationScanOperator seriesAggregationScanOperator = + initAlignedSeriesAggregationScanOperator(aggregators, timeFilter, true, null); + int count = 0; + while (seriesAggregationScanOperator.hasNext()) { + TsBlock resultTsBlock = seriesAggregationScanOperator.next(); + assertTrue(resultTsBlock.getColumn(0).getBoolean(0)); + assertEquals(399, resultTsBlock.getColumn(1).getInt(0)); + assertEquals(20199, resultTsBlock.getColumn(2).getLong(0)); + assertEquals(260.0, resultTsBlock.getColumn(3).getFloat(0), DELTA); + assertEquals(100, resultTsBlock.getColumn(4).getLong(0)); + assertEquals(399, resultTsBlock.getColumn(5).getLong(0)); + count++; + } + assertEquals(1, count); + } + + @Test + public void testGroupByWithoutGlobalTimeFilter() throws IllegalPathException { + int[] result = new int[] {100, 100, 100, 99}; + GroupByTimeParameter groupByTimeParameter = new GroupByTimeParameter(0, 399, 100, 100, true); + List aggregators = new ArrayList<>(); + for (int i = 0; i < measurementSchemas.size(); i++) { + TSDataType dataType = measurementSchemas.get(i).getType(); + List inputLocations = new ArrayList<>(); + inputLocations.add(new InputLocation[] {new InputLocation(0, i)}); + aggregators.add( + new Aggregator( + AccumulatorFactory.createAccumulator(AggregationType.COUNT, dataType, true), + AggregationStep.SINGLE, + inputLocations)); + } + AlignedSeriesAggregationScanOperator seriesAggregationScanOperator = + initAlignedSeriesAggregationScanOperator(aggregators, null, true, groupByTimeParameter); + int count = 0; + while (seriesAggregationScanOperator.hasNext()) { + TsBlock resultTsBlock = seriesAggregationScanOperator.next(); + assertEquals(100 * count, resultTsBlock.getTimeColumn().getLong(0)); + for (int i = 0; i < measurementSchemas.size(); i++) { + assertEquals(result[count], resultTsBlock.getColumn(i).getLong(0)); + } + count++; + } + assertEquals(4, count); + } + + @Test + public void testGroupByWithGlobalTimeFilter() throws IllegalPathException { + int[] result = new int[] {0, 80, 100, 80}; + Filter timeFilter = new AndFilter(TimeFilter.gtEq(120), TimeFilter.ltEq(379)); + GroupByTimeParameter groupByTimeParameter = new GroupByTimeParameter(0, 399, 100, 100, true); + List aggregators = new ArrayList<>(); + for (int i = 0; i < measurementSchemas.size(); i++) { + TSDataType dataType = measurementSchemas.get(i).getType(); + List inputLocations = new ArrayList<>(); + inputLocations.add(new InputLocation[] {new InputLocation(0, i)}); + aggregators.add( + new Aggregator( + AccumulatorFactory.createAccumulator(AggregationType.COUNT, dataType, true), + AggregationStep.SINGLE, + inputLocations)); + } + AlignedSeriesAggregationScanOperator seriesAggregationScanOperator = + initAlignedSeriesAggregationScanOperator( + aggregators, timeFilter, true, groupByTimeParameter); + int count = 0; + while (seriesAggregationScanOperator.hasNext()) { + TsBlock resultTsBlock = seriesAggregationScanOperator.next(); + assertEquals(100 * count, resultTsBlock.getTimeColumn().getLong(0)); + for (int i = 0; i < measurementSchemas.size(); i++) { + assertEquals(result[count], resultTsBlock.getColumn(i).getLong(0)); + } + count++; + } + assertEquals(4, count); + } + + @Test + public void testGroupByWithMultiFunction() throws IllegalPathException { + int[][] result = + new int[][] { + {20000, 20100, 10200, 10300}, + {20099, 20199, 299, 398}, + {20099, 20199, 10259, 10379}, + {20000, 20100, 260, 380} + }; + List aggregationTypes = new ArrayList<>(); + aggregationTypes.add(AggregationType.FIRST_VALUE); + aggregationTypes.add(AggregationType.LAST_VALUE); + aggregationTypes.add(AggregationType.MAX_VALUE); + aggregationTypes.add(AggregationType.MIN_VALUE); + GroupByTimeParameter groupByTimeParameter = new GroupByTimeParameter(0, 399, 100, 100, true); + List aggregators = new ArrayList<>(); + List inputLocations = + Collections.singletonList(new InputLocation[] {new InputLocation(0, 1)}); + AccumulatorFactory.createAccumulators(aggregationTypes, TSDataType.INT32, true) + .forEach(o -> aggregators.add(new Aggregator(o, AggregationStep.SINGLE, inputLocations))); + AlignedSeriesAggregationScanOperator seriesAggregationScanOperator = + initAlignedSeriesAggregationScanOperator(aggregators, null, true, groupByTimeParameter); + int count = 0; + while (seriesAggregationScanOperator.hasNext()) { + TsBlock resultTsBlock = seriesAggregationScanOperator.next(); + assertEquals(100 * count, resultTsBlock.getTimeColumn().getLong(0)); + assertEquals(result[0][count], resultTsBlock.getColumn(0).getInt(0)); + assertEquals(result[1][count], resultTsBlock.getColumn(1).getInt(0)); + assertEquals(result[2][count], resultTsBlock.getColumn(2).getInt(0)); + assertEquals(result[3][count], resultTsBlock.getColumn(3).getInt(0)); + count++; + } + assertEquals(4, count); + } + + @Test + public void testGroupByWithMultiFunctionOrderByTimeDesc() throws IllegalPathException { + int[][] result = + new int[][] { + {20000, 20100, 10200, 10300}, + {20099, 20199, 299, 398}, + {20099, 20199, 10259, 10379}, + {20000, 20100, 260, 380} + }; + List aggregationTypes = new ArrayList<>(); + aggregationTypes.add(AggregationType.FIRST_VALUE); + aggregationTypes.add(AggregationType.LAST_VALUE); + aggregationTypes.add(AggregationType.MAX_VALUE); + aggregationTypes.add(AggregationType.MIN_VALUE); + GroupByTimeParameter groupByTimeParameter = new GroupByTimeParameter(0, 399, 100, 100, true); + List aggregators = new ArrayList<>(); + List inputLocations = + Collections.singletonList(new InputLocation[] {new InputLocation(0, 1)}); + AccumulatorFactory.createAccumulators(aggregationTypes, TSDataType.INT32, false) + .forEach(o -> aggregators.add(new Aggregator(o, AggregationStep.SINGLE, inputLocations))); + AlignedSeriesAggregationScanOperator seriesAggregationScanOperator = + initAlignedSeriesAggregationScanOperator(aggregators, null, false, groupByTimeParameter); + int count = 0; + while (seriesAggregationScanOperator.hasNext()) { + TsBlock resultTsBlock = seriesAggregationScanOperator.next(); + assertEquals(100 * (3 - count), resultTsBlock.getTimeColumn().getLong(0)); + assertEquals(result[0][3 - count], resultTsBlock.getColumn(0).getInt(0)); + assertEquals(result[1][3 - count], resultTsBlock.getColumn(1).getInt(0)); + assertEquals(result[2][3 - count], resultTsBlock.getColumn(2).getInt(0)); + assertEquals(result[3][3 - count], resultTsBlock.getColumn(3).getInt(0)); + count++; + } + assertEquals(4, count); + } + + @Test + public void testGroupBySlidingTimeWindow() throws IllegalPathException { + int[] result = new int[] {50, 50, 50, 50, 50, 50, 50, 49}; + GroupByTimeParameter groupByTimeParameter = new GroupByTimeParameter(0, 399, 100, 50, true); + List aggregationTypes = Collections.singletonList(AggregationType.COUNT); + List aggregators = new ArrayList<>(); + List inputLocations = + Collections.singletonList(new InputLocation[] {new InputLocation(0, 1)}); + AccumulatorFactory.createAccumulators(aggregationTypes, TSDataType.INT32, true) + .forEach(o -> aggregators.add(new Aggregator(o, AggregationStep.SINGLE, inputLocations))); + AlignedSeriesAggregationScanOperator seriesAggregationScanOperator = + initAlignedSeriesAggregationScanOperator(aggregators, null, true, groupByTimeParameter); + int count = 0; + while (seriesAggregationScanOperator.hasNext()) { + TsBlock resultTsBlock = seriesAggregationScanOperator.next(); + assertEquals(50 * count, resultTsBlock.getTimeColumn().getLong(0)); + assertEquals(result[count], resultTsBlock.getColumn(0).getLong(0)); + count++; + } + assertEquals(result.length, count); + } + + @Test + public void testGroupBySlidingTimeWindow2() throws IllegalPathException { + int[] timeColumn = new int[] {0, 20, 30, 50, 60, 80, 90, 110, 120, 140}; + int[] result = new int[] {20, 10, 20, 10, 20, 10, 20, 10, 20, 9}; + GroupByTimeParameter groupByTimeParameter = new GroupByTimeParameter(0, 149, 50, 30, true); + List aggregationTypes = Collections.singletonList(AggregationType.COUNT); + List aggregators = new ArrayList<>(); + List inputLocations = + Collections.singletonList(new InputLocation[] {new InputLocation(0, 1)}); + AccumulatorFactory.createAccumulators(aggregationTypes, TSDataType.INT32, true) + .forEach(o -> aggregators.add(new Aggregator(o, AggregationStep.SINGLE, inputLocations))); + AlignedSeriesAggregationScanOperator seriesAggregationScanOperator = + initAlignedSeriesAggregationScanOperator(aggregators, null, true, groupByTimeParameter); + int count = 0; + while (seriesAggregationScanOperator.hasNext()) { + TsBlock resultTsBlock = seriesAggregationScanOperator.next(); + assertEquals(timeColumn[count], resultTsBlock.getTimeColumn().getLong(0)); + assertEquals(result[count], resultTsBlock.getColumn(0).getLong(0)); + count++; + } + assertEquals(timeColumn.length, count); + } + + @Test + public void testGroupBySlidingWindowWithMultiFunction() throws IllegalPathException { + int[] timeColumn = new int[] {0, 20, 30, 50, 60, 80, 90, 110, 120, 140}; + int[][] result = + new int[][] { + {20000, 20020, 20030, 20050, 20060, 20080, 20090, 20110, 20120, 20140}, + {20019, 20029, 20049, 20059, 20079, 20089, 20109, 20119, 20139, 20148}, + {20019, 20029, 20049, 20059, 20079, 20089, 20109, 20119, 20139, 20148}, + {20000, 20020, 20030, 20050, 20060, 20080, 20090, 20110, 20120, 20140} + }; + List aggregationTypes = new ArrayList<>(); + aggregationTypes.add(AggregationType.FIRST_VALUE); + aggregationTypes.add(AggregationType.LAST_VALUE); + aggregationTypes.add(AggregationType.MAX_VALUE); + aggregationTypes.add(AggregationType.MIN_VALUE); + GroupByTimeParameter groupByTimeParameter = new GroupByTimeParameter(0, 149, 50, 30, true); + List aggregators = new ArrayList<>(); + List inputLocations = + Collections.singletonList(new InputLocation[] {new InputLocation(0, 1)}); + AccumulatorFactory.createAccumulators(aggregationTypes, TSDataType.INT32, true) + .forEach(o -> aggregators.add(new Aggregator(o, AggregationStep.SINGLE, inputLocations))); + AlignedSeriesAggregationScanOperator seriesAggregationScanOperator = + initAlignedSeriesAggregationScanOperator(aggregators, null, true, groupByTimeParameter); + int count = 0; + while (seriesAggregationScanOperator.hasNext()) { + TsBlock resultTsBlock = seriesAggregationScanOperator.next(); + assertEquals(timeColumn[count], resultTsBlock.getTimeColumn().getLong(0)); + assertEquals(result[0][count], resultTsBlock.getColumn(0).getInt(0)); + assertEquals(result[1][count], resultTsBlock.getColumn(1).getInt(0)); + assertEquals(result[2][count], resultTsBlock.getColumn(2).getInt(0)); + assertEquals(result[3][count], resultTsBlock.getColumn(3).getInt(0)); + count++; + } + assertEquals(timeColumn.length, count); + } + + public AlignedSeriesAggregationScanOperator initAlignedSeriesAggregationScanOperator( + List aggregators, + Filter timeFilter, + boolean ascending, + GroupByTimeParameter groupByTimeParameter) + throws IllegalPathException { + AlignedPath alignedPath = + new AlignedPath( + SERIES_AGGREGATION_SCAN_OPERATOR_TEST_SG + ".device0", + measurementSchemas.stream() + .map(MeasurementSchema::getMeasurementId) + .collect(Collectors.toList()), + measurementSchemas.stream() + .map(m -> (IMeasurementSchema) m) + .collect(Collectors.toList())); + Set allSensors = Sets.newHashSet("sensor0"); + QueryId queryId = new QueryId("stub_query"); + FragmentInstanceId instanceId = + new FragmentInstanceId(new PlanFragmentId(queryId, 0), "stub-instance"); + FragmentInstanceStateMachine stateMachine = + new FragmentInstanceStateMachine(instanceId, instanceNotificationExecutor); + FragmentInstanceContext fragmentInstanceContext = + createFragmentInstanceContext(instanceId, stateMachine); + PlanNodeId planNodeId = new PlanNodeId("1"); + fragmentInstanceContext.addOperatorContext( + 1, planNodeId, SeriesScanOperator.class.getSimpleName()); + + AlignedSeriesAggregationScanOperator seriesAggregationScanOperator = + new AlignedSeriesAggregationScanOperator( + planNodeId, + alignedPath, + fragmentInstanceContext.getOperatorContexts().get(0), + aggregators, + timeFilter, + ascending, + groupByTimeParameter); + seriesAggregationScanOperator.initQueryDataSource( + new QueryDataSource(seqResources, unSeqResources)); + return seriesAggregationScanOperator; + } +} diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/RawDataAggregateOperatorTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/RawDataAggregationOperatorTest.java similarity index 90% rename from server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/RawDataAggregateOperatorTest.java rename to server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/RawDataAggregationOperatorTest.java index 0de2bd6b353c..daed697a1344 100644 --- a/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/RawDataAggregateOperatorTest.java +++ b/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/RawDataAggregationOperatorTest.java @@ -33,7 +33,7 @@ import org.apache.iotdb.db.mpp.common.QueryId; import org.apache.iotdb.db.mpp.execution.fragment.FragmentInstanceContext; import org.apache.iotdb.db.mpp.execution.fragment.FragmentInstanceStateMachine; -import org.apache.iotdb.db.mpp.execution.operator.process.RawDataAggregateOperator; +import org.apache.iotdb.db.mpp.execution.operator.process.RawDataAggregationOperator; import org.apache.iotdb.db.mpp.execution.operator.process.TimeJoinOperator; import org.apache.iotdb.db.mpp.execution.operator.process.merge.AscTimeComparator; import org.apache.iotdb.db.mpp.execution.operator.process.merge.SingleColumnMerger; @@ -65,9 +65,9 @@ import static org.apache.iotdb.db.mpp.execution.fragment.FragmentInstanceContext.createFragmentInstanceContext; import static org.junit.Assert.assertEquals; -public class RawDataAggregateOperatorTest { +public class RawDataAggregationOperatorTest { - private static final String AGGREGATE_OPERATOR_TEST_SG = "root.RawDataAggregateOperatorTest"; + private static final String AGGREGATION_OPERATOR_TEST_SG = "root.RawDataAggregationOperatorTest"; private final List deviceIds = new ArrayList<>(); private final List measurementSchemas = new ArrayList<>(); @@ -78,7 +78,7 @@ public class RawDataAggregateOperatorTest { @Before public void setUp() throws MetadataException, IOException, WriteProcessException { SeriesReaderTestUtil.setUp( - measurementSchemas, deviceIds, seqResources, unSeqResources, AGGREGATE_OPERATOR_TEST_SG); + measurementSchemas, deviceIds, seqResources, unSeqResources, AGGREGATION_OPERATOR_TEST_SG); this.instanceNotificationExecutor = IoTDBThreadPoolFactory.newFixedThreadPool(1, "test-instance-notification"); } @@ -117,11 +117,11 @@ public void aggregateRawDataTest1() throws IllegalPathException { } } - RawDataAggregateOperator rawDataAggregateOperator = - initRawDataAggregateOperator(aggregationTypes, null, inputLocations); + RawDataAggregationOperator rawDataAggregationOperator = + initRawDataAggregationOperator(aggregationTypes, null, inputLocations); int count = 0; - while (rawDataAggregateOperator.hasNext()) { - TsBlock resultTsBlock = rawDataAggregateOperator.next(); + while (rawDataAggregationOperator.hasNext()) { + TsBlock resultTsBlock = rawDataAggregationOperator.next(); for (int i = 0; i < 2; i++) { assertEquals(500, resultTsBlock.getColumn(6 * i).getLong(0)); assertEquals(6524750.0, resultTsBlock.getColumn(6 * i + 1).getDouble(0), 0.0001); @@ -167,11 +167,11 @@ public void aggregateRawDataTest2() throws IllegalPathException { inputLocations.add(inputLocationForOneAggregator); } - RawDataAggregateOperator rawDataAggregateOperator = - initRawDataAggregateOperator(aggregationTypes, null, inputLocations); + RawDataAggregationOperator rawDataAggregationOperator = + initRawDataAggregationOperator(aggregationTypes, null, inputLocations); int count = 0; - while (rawDataAggregateOperator.hasNext()) { - TsBlock resultTsBlock = rawDataAggregateOperator.next(); + while (rawDataAggregationOperator.hasNext()) { + TsBlock resultTsBlock = rawDataAggregationOperator.next(); for (int i = 0; i < 2; i++) { assertEquals(13049.5, resultTsBlock.getColumn(i).getDouble(0), 0.001); } @@ -215,11 +215,11 @@ public void groupByRawDataTest1() throws IllegalPathException { } GroupByTimeParameter groupByTimeParameter = new GroupByTimeParameter(0, 399, 100, 100, true); - RawDataAggregateOperator rawDataAggregateOperator = - initRawDataAggregateOperator(aggregationTypes, groupByTimeParameter, inputLocations); + RawDataAggregationOperator rawDataAggregationOperator = + initRawDataAggregationOperator(aggregationTypes, groupByTimeParameter, inputLocations); int count = 0; - while (rawDataAggregateOperator.hasNext()) { - TsBlock resultTsBlock = rawDataAggregateOperator.next(); + while (rawDataAggregationOperator.hasNext()) { + TsBlock resultTsBlock = rawDataAggregationOperator.next(); assertEquals(100 * count, resultTsBlock.getTimeColumn().getLong(0)); for (int i = 0; i < 2; i++) { assertEquals(result[0][count], resultTsBlock.getColumn(6 * i).getLong(0)); @@ -264,11 +264,11 @@ public void groupByRawDataTest2() throws IllegalPathException { inputLocations.add(inputLocationForOneAggregator); } GroupByTimeParameter groupByTimeParameter = new GroupByTimeParameter(0, 399, 100, 100, true); - RawDataAggregateOperator rawDataAggregateOperator = - initRawDataAggregateOperator(aggregationTypes, groupByTimeParameter, inputLocations); + RawDataAggregationOperator rawDataAggregationOperator = + initRawDataAggregationOperator(aggregationTypes, groupByTimeParameter, inputLocations); int count = 0; - while (rawDataAggregateOperator.hasNext()) { - TsBlock resultTsBlock = rawDataAggregateOperator.next(); + while (rawDataAggregationOperator.hasNext()) { + TsBlock resultTsBlock = rawDataAggregationOperator.next(); assertEquals(100 * count, resultTsBlock.getTimeColumn().getLong(0)); for (int i = 0; i < 2; i++) { assertEquals(result[0][count], resultTsBlock.getColumn(i).getDouble(0), 0.001); @@ -284,7 +284,7 @@ public void groupByRawDataTest2() throws IllegalPathException { assertEquals(4, count); } - private RawDataAggregateOperator initRawDataAggregateOperator( + private RawDataAggregationOperator initRawDataAggregationOperator( List aggregationTypes, GroupByTimeParameter groupByTimeParameter, List> inputLocations) @@ -293,7 +293,7 @@ private RawDataAggregateOperator initRawDataAggregateOperator( IoTDBThreadPoolFactory.newFixedThreadPool(1, "test-instance-notification"); MeasurementPath measurementPath1 = - new MeasurementPath(AGGREGATE_OPERATOR_TEST_SG + ".device0.sensor0", TSDataType.INT32); + new MeasurementPath(AGGREGATION_OPERATOR_TEST_SG + ".device0.sensor0", TSDataType.INT32); Set allSensors = new HashSet<>(); allSensors.add("sensor0"); allSensors.add("sensor1"); @@ -313,7 +313,7 @@ private RawDataAggregateOperator initRawDataAggregateOperator( fragmentInstanceContext.addOperatorContext( 3, new PlanNodeId("3"), TimeJoinOperator.class.getSimpleName()); fragmentInstanceContext.addOperatorContext( - 4, new PlanNodeId("4"), RawDataAggregateOperatorTest.class.getSimpleName()); + 4, new PlanNodeId("4"), RawDataAggregationOperatorTest.class.getSimpleName()); SeriesScanOperator seriesScanOperator1 = new SeriesScanOperator( planNodeId1, @@ -327,7 +327,7 @@ private RawDataAggregateOperator initRawDataAggregateOperator( seriesScanOperator1.initQueryDataSource(new QueryDataSource(seqResources, unSeqResources)); MeasurementPath measurementPath2 = - new MeasurementPath(AGGREGATE_OPERATOR_TEST_SG + ".device0.sensor1", TSDataType.INT32); + new MeasurementPath(AGGREGATION_OPERATOR_TEST_SG + ".device0.sensor1", TSDataType.INT32); SeriesScanOperator seriesScanOperator2 = new SeriesScanOperator( planNodeId2, @@ -358,7 +358,7 @@ private RawDataAggregateOperator initRawDataAggregateOperator( aggregators.add( new Aggregator(accumulators.get(i), AggregationStep.SINGLE, inputLocations.get(i))); } - return new RawDataAggregateOperator( + return new RawDataAggregationOperator( fragmentInstanceContext.getOperatorContexts().get(3), aggregators, timeJoinOperator, diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/SeriesAggregateScanOperatorTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/SeriesAggregationScanOperatorTest.java similarity index 80% rename from server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/SeriesAggregateScanOperatorTest.java rename to server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/SeriesAggregationScanOperatorTest.java index 1c7a77ded6e4..0c62a4632881 100644 --- a/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/SeriesAggregateScanOperatorTest.java +++ b/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/SeriesAggregationScanOperatorTest.java @@ -30,9 +30,8 @@ import org.apache.iotdb.db.mpp.common.PlanFragmentId; import org.apache.iotdb.db.mpp.common.QueryId; import org.apache.iotdb.db.mpp.execution.fragment.FragmentInstanceContext; -import org.apache.iotdb.db.mpp.execution.fragment.FragmentInstanceState; import org.apache.iotdb.db.mpp.execution.fragment.FragmentInstanceStateMachine; -import org.apache.iotdb.db.mpp.execution.operator.source.SeriesAggregateScanOperator; +import org.apache.iotdb.db.mpp.execution.operator.source.SeriesAggregationScanOperator; import org.apache.iotdb.db.mpp.execution.operator.source.SeriesScanOperator; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId; import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.AggregationStep; @@ -58,12 +57,11 @@ import java.util.List; import java.util.Set; import java.util.concurrent.ExecutorService; -import java.util.concurrent.atomic.AtomicReference; import static org.apache.iotdb.db.mpp.execution.fragment.FragmentInstanceContext.createFragmentInstanceContext; import static org.junit.Assert.assertEquals; -public class SeriesAggregateScanOperatorTest { +public class SeriesAggregationScanOperatorTest { private static final String SERIES_SCAN_OPERATOR_TEST_SG = "root.SeriesScanOperatorTest"; private final List deviceIds = new ArrayList<>(); @@ -93,11 +91,11 @@ public void testAggregationWithoutTimeFilter() throws IllegalPathException { List aggregators = new ArrayList<>(); AccumulatorFactory.createAccumulators(aggregationTypes, TSDataType.INT32, true) .forEach(o -> aggregators.add(new Aggregator(o, AggregationStep.SINGLE))); - SeriesAggregateScanOperator seriesAggregateScanOperator = - initSeriesAggregateScanOperator(aggregators, null, true, null); + SeriesAggregationScanOperator seriesAggregationScanOperator = + initSeriesAggregationScanOperator(aggregators, null, true, null); int count = 0; - while (seriesAggregateScanOperator.hasNext()) { - TsBlock resultTsBlock = seriesAggregateScanOperator.next(); + while (seriesAggregationScanOperator.hasNext()) { + TsBlock resultTsBlock = seriesAggregationScanOperator.next(); assertEquals(500, resultTsBlock.getColumn(0).getLong(0)); count++; } @@ -110,11 +108,11 @@ public void testAggregationWithoutTimeFilterOrderByTimeDesc() throws IllegalPath List aggregators = new ArrayList<>(); AccumulatorFactory.createAccumulators(aggregationTypes, TSDataType.INT32, true) .forEach(o -> aggregators.add(new Aggregator(o, AggregationStep.SINGLE))); - SeriesAggregateScanOperator seriesAggregateScanOperator = - initSeriesAggregateScanOperator(aggregators, null, false, null); + SeriesAggregationScanOperator seriesAggregationScanOperator = + initSeriesAggregationScanOperator(aggregators, null, false, null); int count = 0; - while (seriesAggregateScanOperator.hasNext()) { - TsBlock resultTsBlock = seriesAggregateScanOperator.next(); + while (seriesAggregationScanOperator.hasNext()) { + TsBlock resultTsBlock = seriesAggregationScanOperator.next(); assertEquals(500, resultTsBlock.getColumn(0).getLong(0)); count++; } @@ -129,11 +127,11 @@ public void testMultiAggregationFuncWithoutTimeFilter1() throws IllegalPathExcep List aggregators = new ArrayList<>(); AccumulatorFactory.createAccumulators(aggregationTypes, TSDataType.INT32, true) .forEach(o -> aggregators.add(new Aggregator(o, AggregationStep.SINGLE))); - SeriesAggregateScanOperator seriesAggregateScanOperator = - initSeriesAggregateScanOperator(aggregators, null, true, null); + SeriesAggregationScanOperator seriesAggregationScanOperator = + initSeriesAggregationScanOperator(aggregators, null, true, null); int count = 0; - while (seriesAggregateScanOperator.hasNext()) { - TsBlock resultTsBlock = seriesAggregateScanOperator.next(); + while (seriesAggregationScanOperator.hasNext()) { + TsBlock resultTsBlock = seriesAggregationScanOperator.next(); assertEquals(500, resultTsBlock.getColumn(0).getLong(0)); assertEquals(6524750.0, resultTsBlock.getColumn(1).getDouble(0), 0.0001); count++; @@ -153,11 +151,11 @@ public void testMultiAggregationFuncWithoutTimeFilter2() throws IllegalPathExcep List aggregators = new ArrayList<>(); AccumulatorFactory.createAccumulators(aggregationTypes, TSDataType.INT32, true) .forEach(o -> aggregators.add(new Aggregator(o, AggregationStep.SINGLE))); - SeriesAggregateScanOperator seriesAggregateScanOperator = - initSeriesAggregateScanOperator(aggregators, null, true, null); + SeriesAggregationScanOperator seriesAggregationScanOperator = + initSeriesAggregationScanOperator(aggregators, null, true, null); int count = 0; - while (seriesAggregateScanOperator.hasNext()) { - TsBlock resultTsBlock = seriesAggregateScanOperator.next(); + while (seriesAggregationScanOperator.hasNext()) { + TsBlock resultTsBlock = seriesAggregationScanOperator.next(); assertEquals(20000, resultTsBlock.getColumn(0).getInt(0)); assertEquals(10499, resultTsBlock.getColumn(1).getInt(0)); assertEquals(0, resultTsBlock.getColumn(2).getLong(0)); @@ -182,11 +180,11 @@ public void testMultiAggregationFuncWithoutTimeFilterOrderByTimeDesc() List aggregators = new ArrayList<>(); AccumulatorFactory.createAccumulators(aggregationTypes, TSDataType.INT32, false) .forEach(o -> aggregators.add(new Aggregator(o, AggregationStep.SINGLE))); - SeriesAggregateScanOperator seriesAggregateScanOperator = - initSeriesAggregateScanOperator(aggregators, null, false, null); + SeriesAggregationScanOperator seriesAggregationScanOperator = + initSeriesAggregationScanOperator(aggregators, null, false, null); int count = 0; - while (seriesAggregateScanOperator.hasNext()) { - TsBlock resultTsBlock = seriesAggregateScanOperator.next(); + while (seriesAggregationScanOperator.hasNext()) { + TsBlock resultTsBlock = seriesAggregationScanOperator.next(); assertEquals(20000, resultTsBlock.getColumn(0).getInt(0)); assertEquals(10499, resultTsBlock.getColumn(1).getInt(0)); assertEquals(0, resultTsBlock.getColumn(2).getLong(0)); @@ -205,11 +203,11 @@ public void testAggregationWithTimeFilter1() throws IllegalPathException { AccumulatorFactory.createAccumulators(aggregationTypes, TSDataType.INT32, true) .forEach(o -> aggregators.add(new Aggregator(o, AggregationStep.SINGLE))); Filter timeFilter = TimeFilter.gtEq(120); - SeriesAggregateScanOperator seriesAggregateScanOperator = - initSeriesAggregateScanOperator(aggregators, timeFilter, true, null); + SeriesAggregationScanOperator seriesAggregationScanOperator = + initSeriesAggregationScanOperator(aggregators, timeFilter, true, null); int count = 0; - while (seriesAggregateScanOperator.hasNext()) { - TsBlock resultTsBlock = seriesAggregateScanOperator.next(); + while (seriesAggregationScanOperator.hasNext()) { + TsBlock resultTsBlock = seriesAggregationScanOperator.next(); assertEquals(resultTsBlock.getColumn(0).getLong(0), 380); count++; } @@ -223,11 +221,11 @@ public void testAggregationWithTimeFilter2() throws IllegalPathException { List aggregators = new ArrayList<>(); AccumulatorFactory.createAccumulators(aggregationTypes, TSDataType.INT32, true) .forEach(o -> aggregators.add(new Aggregator(o, AggregationStep.SINGLE))); - SeriesAggregateScanOperator seriesAggregateScanOperator = - initSeriesAggregateScanOperator(aggregators, timeFilter, true, null); + SeriesAggregationScanOperator seriesAggregationScanOperator = + initSeriesAggregationScanOperator(aggregators, timeFilter, true, null); int count = 0; - while (seriesAggregateScanOperator.hasNext()) { - TsBlock resultTsBlock = seriesAggregateScanOperator.next(); + while (seriesAggregationScanOperator.hasNext()) { + TsBlock resultTsBlock = seriesAggregationScanOperator.next(); assertEquals(resultTsBlock.getColumn(0).getLong(0), 380); count++; } @@ -241,11 +239,11 @@ public void testAggregationWithTimeFilter3() throws IllegalPathException { List aggregators = new ArrayList<>(); AccumulatorFactory.createAccumulators(aggregationTypes, TSDataType.INT32, true) .forEach(o -> aggregators.add(new Aggregator(o, AggregationStep.SINGLE))); - SeriesAggregateScanOperator seriesAggregateScanOperator = - initSeriesAggregateScanOperator(aggregators, timeFilter, true, null); + SeriesAggregationScanOperator seriesAggregationScanOperator = + initSeriesAggregationScanOperator(aggregators, timeFilter, true, null); int count = 0; - while (seriesAggregateScanOperator.hasNext()) { - TsBlock resultTsBlock = seriesAggregateScanOperator.next(); + while (seriesAggregationScanOperator.hasNext()) { + TsBlock resultTsBlock = seriesAggregationScanOperator.next(); assertEquals(resultTsBlock.getColumn(0).getLong(0), 300); count++; } @@ -265,11 +263,11 @@ public void testMultiAggregationWithTimeFilter() throws IllegalPathException { AccumulatorFactory.createAccumulators(aggregationTypes, TSDataType.INT32, true) .forEach(o -> aggregators.add(new Aggregator(o, AggregationStep.SINGLE))); Filter timeFilter = new AndFilter(TimeFilter.gtEq(100), TimeFilter.ltEq(399)); - SeriesAggregateScanOperator seriesAggregateScanOperator = - initSeriesAggregateScanOperator(aggregators, timeFilter, true, null); + SeriesAggregationScanOperator seriesAggregationScanOperator = + initSeriesAggregationScanOperator(aggregators, timeFilter, true, null); int count = 0; - while (seriesAggregateScanOperator.hasNext()) { - TsBlock resultTsBlock = seriesAggregateScanOperator.next(); + while (seriesAggregationScanOperator.hasNext()) { + TsBlock resultTsBlock = seriesAggregationScanOperator.next(); assertEquals(20100, resultTsBlock.getColumn(0).getInt(0)); assertEquals(399, resultTsBlock.getColumn(1).getInt(0)); assertEquals(100, resultTsBlock.getColumn(2).getLong(0)); @@ -289,11 +287,11 @@ public void testGroupByWithoutGlobalTimeFilter() throws IllegalPathException { List aggregators = new ArrayList<>(); AccumulatorFactory.createAccumulators(aggregationTypes, TSDataType.INT32, true) .forEach(o -> aggregators.add(new Aggregator(o, AggregationStep.SINGLE))); - SeriesAggregateScanOperator seriesAggregateScanOperator = - initSeriesAggregateScanOperator(aggregators, null, true, groupByTimeParameter); + SeriesAggregationScanOperator seriesAggregationScanOperator = + initSeriesAggregationScanOperator(aggregators, null, true, groupByTimeParameter); int count = 0; - while (seriesAggregateScanOperator.hasNext()) { - TsBlock resultTsBlock = seriesAggregateScanOperator.next(); + while (seriesAggregationScanOperator.hasNext()) { + TsBlock resultTsBlock = seriesAggregationScanOperator.next(); assertEquals(100 * count, resultTsBlock.getTimeColumn().getLong(0)); assertEquals(result[count], resultTsBlock.getColumn(0).getLong(0)); count++; @@ -310,11 +308,11 @@ public void testGroupByWithGlobalTimeFilter() throws IllegalPathException { List aggregators = new ArrayList<>(); AccumulatorFactory.createAccumulators(aggregationTypes, TSDataType.INT32, true) .forEach(o -> aggregators.add(new Aggregator(o, AggregationStep.SINGLE))); - SeriesAggregateScanOperator seriesAggregateScanOperator = - initSeriesAggregateScanOperator(aggregators, timeFilter, true, groupByTimeParameter); + SeriesAggregationScanOperator seriesAggregationScanOperator = + initSeriesAggregationScanOperator(aggregators, timeFilter, true, groupByTimeParameter); int count = 0; - while (seriesAggregateScanOperator.hasNext()) { - TsBlock resultTsBlock = seriesAggregateScanOperator.next(); + while (seriesAggregationScanOperator.hasNext()) { + TsBlock resultTsBlock = seriesAggregationScanOperator.next(); assertEquals(100 * count, resultTsBlock.getTimeColumn().getLong(0)); assertEquals(result[count], resultTsBlock.getColumn(0).getLong(0)); count++; @@ -340,11 +338,11 @@ public void testGroupByWithMultiFunction() throws IllegalPathException { List aggregators = new ArrayList<>(); AccumulatorFactory.createAccumulators(aggregationTypes, TSDataType.INT32, true) .forEach(o -> aggregators.add(new Aggregator(o, AggregationStep.SINGLE))); - SeriesAggregateScanOperator seriesAggregateScanOperator = - initSeriesAggregateScanOperator(aggregators, null, true, groupByTimeParameter); + SeriesAggregationScanOperator seriesAggregationScanOperator = + initSeriesAggregationScanOperator(aggregators, null, true, groupByTimeParameter); int count = 0; - while (seriesAggregateScanOperator.hasNext()) { - TsBlock resultTsBlock = seriesAggregateScanOperator.next(); + while (seriesAggregationScanOperator.hasNext()) { + TsBlock resultTsBlock = seriesAggregationScanOperator.next(); assertEquals(100 * count, resultTsBlock.getTimeColumn().getLong(0)); assertEquals(result[0][count], resultTsBlock.getColumn(0).getInt(0)); assertEquals(result[1][count], resultTsBlock.getColumn(1).getInt(0)); @@ -373,11 +371,11 @@ public void testGroupByWithMultiFunctionOrderByTimeDesc() throws IllegalPathExce List aggregators = new ArrayList<>(); AccumulatorFactory.createAccumulators(aggregationTypes, TSDataType.INT32, false) .forEach(o -> aggregators.add(new Aggregator(o, AggregationStep.SINGLE))); - SeriesAggregateScanOperator seriesAggregateScanOperator = - initSeriesAggregateScanOperator(aggregators, null, false, groupByTimeParameter); + SeriesAggregationScanOperator seriesAggregationScanOperator = + initSeriesAggregationScanOperator(aggregators, null, false, groupByTimeParameter); int count = 0; - while (seriesAggregateScanOperator.hasNext()) { - TsBlock resultTsBlock = seriesAggregateScanOperator.next(); + while (seriesAggregationScanOperator.hasNext()) { + TsBlock resultTsBlock = seriesAggregationScanOperator.next(); assertEquals(100 * (3 - count), resultTsBlock.getTimeColumn().getLong(0)); assertEquals(result[0][3 - count], resultTsBlock.getColumn(0).getInt(0)); assertEquals(result[1][3 - count], resultTsBlock.getColumn(1).getInt(0)); @@ -396,11 +394,11 @@ public void testGroupBySlidingTimeWindow() throws IllegalPathException { List aggregators = new ArrayList<>(); AccumulatorFactory.createAccumulators(aggregationTypes, TSDataType.INT32, true) .forEach(o -> aggregators.add(new Aggregator(o, AggregationStep.SINGLE))); - SeriesAggregateScanOperator seriesAggregateScanOperator = - initSeriesAggregateScanOperator(aggregators, null, true, groupByTimeParameter); + SeriesAggregationScanOperator seriesAggregationScanOperator = + initSeriesAggregationScanOperator(aggregators, null, true, groupByTimeParameter); int count = 0; - while (seriesAggregateScanOperator.hasNext()) { - TsBlock resultTsBlock = seriesAggregateScanOperator.next(); + while (seriesAggregationScanOperator.hasNext()) { + TsBlock resultTsBlock = seriesAggregationScanOperator.next(); assertEquals(50 * count, resultTsBlock.getTimeColumn().getLong(0)); assertEquals(result[count], resultTsBlock.getColumn(0).getLong(0)); count++; @@ -417,11 +415,11 @@ public void testGroupBySlidingTimeWindow2() throws IllegalPathException { List aggregators = new ArrayList<>(); AccumulatorFactory.createAccumulators(aggregationTypes, TSDataType.INT32, true) .forEach(o -> aggregators.add(new Aggregator(o, AggregationStep.SINGLE))); - SeriesAggregateScanOperator seriesAggregateScanOperator = - initSeriesAggregateScanOperator(aggregators, null, true, groupByTimeParameter); + SeriesAggregationScanOperator seriesAggregationScanOperator = + initSeriesAggregationScanOperator(aggregators, null, true, groupByTimeParameter); int count = 0; - while (seriesAggregateScanOperator.hasNext()) { - TsBlock resultTsBlock = seriesAggregateScanOperator.next(); + while (seriesAggregationScanOperator.hasNext()) { + TsBlock resultTsBlock = seriesAggregationScanOperator.next(); assertEquals(timeColumn[count], resultTsBlock.getTimeColumn().getLong(0)); assertEquals(result[count], resultTsBlock.getColumn(0).getLong(0)); count++; @@ -448,11 +446,11 @@ public void testGroupBySlidingWindowWithMultiFunction() throws IllegalPathExcept List aggregators = new ArrayList<>(); AccumulatorFactory.createAccumulators(aggregationTypes, TSDataType.INT32, true) .forEach(o -> aggregators.add(new Aggregator(o, AggregationStep.SINGLE))); - SeriesAggregateScanOperator seriesAggregateScanOperator = - initSeriesAggregateScanOperator(aggregators, null, true, groupByTimeParameter); + SeriesAggregationScanOperator seriesAggregationScanOperator = + initSeriesAggregationScanOperator(aggregators, null, true, groupByTimeParameter); int count = 0; - while (seriesAggregateScanOperator.hasNext()) { - TsBlock resultTsBlock = seriesAggregateScanOperator.next(); + while (seriesAggregationScanOperator.hasNext()) { + TsBlock resultTsBlock = seriesAggregationScanOperator.next(); assertEquals(timeColumn[count], resultTsBlock.getTimeColumn().getLong(0)); assertEquals(result[0][count], resultTsBlock.getColumn(0).getInt(0)); assertEquals(result[1][count], resultTsBlock.getColumn(1).getInt(0)); @@ -463,7 +461,7 @@ public void testGroupBySlidingWindowWithMultiFunction() throws IllegalPathExcept assertEquals(timeColumn.length, count); } - public SeriesAggregateScanOperator initSeriesAggregateScanOperator( + public SeriesAggregationScanOperator initSeriesAggregationScanOperator( List aggregators, Filter timeFilter, boolean ascending, @@ -473,8 +471,6 @@ public SeriesAggregateScanOperator initSeriesAggregateScanOperator( new MeasurementPath(SERIES_SCAN_OPERATOR_TEST_SG + ".device0.sensor0", TSDataType.INT32); Set allSensors = Sets.newHashSet("sensor0"); QueryId queryId = new QueryId("stub_query"); - AtomicReference state = - new AtomicReference<>(FragmentInstanceState.RUNNING); FragmentInstanceId instanceId = new FragmentInstanceId(new PlanFragmentId(queryId, 0), "stub-instance"); FragmentInstanceStateMachine stateMachine = @@ -485,8 +481,8 @@ public SeriesAggregateScanOperator initSeriesAggregateScanOperator( fragmentInstanceContext.addOperatorContext( 1, planNodeId, SeriesScanOperator.class.getSimpleName()); - SeriesAggregateScanOperator seriesAggregateScanOperator = - new SeriesAggregateScanOperator( + SeriesAggregationScanOperator seriesAggregationScanOperator = + new SeriesAggregationScanOperator( planNodeId, measurementPath, allSensors, @@ -495,8 +491,8 @@ public SeriesAggregateScanOperator initSeriesAggregateScanOperator( timeFilter, ascending, groupByTimeParameter); - seriesAggregateScanOperator.initQueryDataSource( + seriesAggregationScanOperator.initQueryDataSource( new QueryDataSource(seqResources, unSeqResources)); - return seriesAggregateScanOperator; + return seriesAggregationScanOperator; } } From bda7cfa7bc165f0f929491f0f6f8e0a93fbf65fd Mon Sep 17 00:00:00 2001 From: liuminghui233 <36565497+liuminghui233@users.noreply.github.com> Date: Fri, 20 May 2022 16:22:08 +0800 Subject: [PATCH 060/436] [IOTDB-3205] [IOTDB-3208] Align by device query & aggregate query support nested expressions and UDFs (#5934) --- .../iotdb/db/metadata/utils/MetaUtils.java | 26 +- .../db/mpp/common/header/DatasetHeader.java | 4 + .../iotdb/db/mpp/plan/analyze/Analysis.java | 183 ++++-- .../iotdb/db/mpp/plan/analyze/Analyzer.java | 582 +++++++++--------- .../mpp/plan/analyze/ConcatPathRewriter.java | 65 +- .../mpp/plan/analyze/ExpressionAnalyzer.java | 402 ++++++------ .../db/mpp/plan/execution/QueryExecution.java | 2 +- .../execution/config/ConfigExecution.java | 2 +- .../iotdb/db/mpp/plan/parser/ASTVisitor.java | 4 +- .../mpp/plan/parser/StatementGenerator.java | 4 +- .../plan/planner/LocalExecutionPlanner.java | 12 +- .../mpp/plan/planner/LogicalPlanBuilder.java | 157 +++-- .../db/mpp/plan/planner/LogicalPlanner.java | 55 +- .../statement/component/ResultColumn.java | 18 +- .../statement/component/SelectComponent.java | 23 +- .../plan/statement/crud/QueryStatement.java | 42 +- .../db/mpp/plan/analyze/AnalyzeFailTest.java | 18 + .../db/mpp/plan/plan/LogicalPlannerTest.java | 1 + .../mpp/plan/plan/QueryLogicalPlanUtil.java | 346 ++++++----- 19 files changed, 1044 insertions(+), 902 deletions(-) diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/utils/MetaUtils.java b/server/src/main/java/org/apache/iotdb/db/metadata/utils/MetaUtils.java index ad2778960c1f..96189b85513f 100644 --- a/server/src/main/java/org/apache/iotdb/db/metadata/utils/MetaUtils.java +++ b/server/src/main/java/org/apache/iotdb/db/metadata/utils/MetaUtils.java @@ -194,28 +194,34 @@ public static Map>> groupAlignedSeriesWithAggreg public static Map> groupAlignedAggregations( Map> pathToAggregations) { Map> result = new HashMap<>(); - List alignedPathAggregations = new ArrayList<>(); - AlignedPath alignedPath = null; + Map> deviceToAlignedPathsMap = new HashMap<>(); for (PartialPath path : pathToAggregations.keySet()) { MeasurementPath measurementPath = (MeasurementPath) path; if (!measurementPath.isUnderAlignedEntity()) { result .computeIfAbsent(measurementPath, key -> new ArrayList<>()) .addAll(pathToAggregations.get(path)); - alignedPath = null; - alignedPathAggregations.clear(); } else { - if (alignedPath == null || !alignedPath.equals(measurementPath.getDevice())) { + deviceToAlignedPathsMap + .computeIfAbsent(path.getDevice(), key -> new ArrayList<>()) + .add(measurementPath); + } + } + for (Map.Entry> alignedPathEntry : + deviceToAlignedPathsMap.entrySet()) { + List measurementPathList = alignedPathEntry.getValue(); + AlignedPath alignedPath = null; + List aggregationDescriptorList = new ArrayList<>(); + for (int i = 0; i < measurementPathList.size(); i++) { + MeasurementPath measurementPath = measurementPathList.get(i); + if (i == 0) { alignedPath = new AlignedPath(measurementPath); - alignedPathAggregations.addAll(pathToAggregations.get(path)); } else { alignedPath.addMeasurement(measurementPath); - alignedPathAggregations.addAll(pathToAggregations.get(path)); } + aggregationDescriptorList.addAll(pathToAggregations.get(measurementPath)); } - } - if (alignedPath != null) { - result.put(alignedPath, alignedPathAggregations); + result.put(alignedPath, aggregationDescriptorList); } return result; } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/common/header/DatasetHeader.java b/server/src/main/java/org/apache/iotdb/db/mpp/common/header/DatasetHeader.java index f5dd5c669a57..7f21dc469f0e 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/common/header/DatasetHeader.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/common/header/DatasetHeader.java @@ -91,4 +91,8 @@ public List getRespAliasColumns() { public Map getColumnNameIndexMap() { return columnToTsBlockIndexMap; } + + public int getOutputValueColumnCount() { + return (int) columnHeaders.stream().map(ColumnHeader::getColumnName).distinct().count(); + } } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analysis.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analysis.java index 80f4d9ac9762..ded3afa59c73 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analysis.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analysis.java @@ -39,6 +39,10 @@ /** Analysis used for planning a query. TODO: This class may need to store more info for a query. */ public class Analysis { + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Common Analysis + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Statement private Statement statement; @@ -54,46 +58,84 @@ public class Analysis { // map from output column name (for every node) to its datatype private TypeProvider typeProvider; + private boolean finishQueryAfterAnalyze; + + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Query Analysis (used in ALIGN BY TIME) + ///////////////////////////////////////////////////////////////////////////////////////////////// + // map from device name to series/aggregation under this device - private Map> sourceExpressions; + private Set sourceExpressions; - // expression of output column to be calculated - private Set selectExpressions; + // input expressions of aggregations to be calculated + private Set aggregationTransformExpressions; // all aggregations that need to be calculated - private Map> aggregationExpressions; + private Set aggregationExpressions; + + // expression of output column to be calculated + private Set transformExpressions; + + private Expression queryFilter; // map from grouped path name to list of input aggregation in `GROUP BY LEVEL` clause private Map> groupByLevelExpressions; - // parameter of `WITHOUT NULL` clause - private FilterNullParameter filterNullParameter; + private boolean isRawDataSource; - // parameter of `FILL` clause - private FillDescriptor fillDescriptor; + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Query Analysis (used in ALIGN BY DEVICE) + ///////////////////////////////////////////////////////////////////////////////////////////////// - // parameter of `GROUP BY TIME` clause - private GroupByTimeParameter groupByTimeParameter; + // map from device name to series/aggregation under this device + private Map> deviceToSourceExpressions; - private Expression queryFilter; + // input expressions of aggregations to be calculated + private Map> deviceToAggregationTransformExpressions; + + // all aggregations that need to be calculated + private Map> deviceToAggregationExpressions; - // map from device name to query filter under this device (used in ALIGN BY DEVICE) + // expression of output column to be calculated + private Map> deviceToTransformExpressions; + + // map from device name to query filter under this device private Map deviceToQueryFilter; + // e.g. [s1,s2,s3] is query, but [s1, s3] exists in device1, then device1 -> [1, 3], s1 is 1 but + // not 0 because device is the first column + private Map> deviceToMeasurementIndexesMap; + + private Map deviceToIsRawDataSource; + + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Query Common Analysis (above DeviceView) + ///////////////////////////////////////////////////////////////////////////////////////////////// + // indicate is there a value filter private boolean hasValueFilter = false; + // true if nested expressions and UDFs exist in aggregation function + private boolean isHasRawDataInputAggregation; + // a global time filter used in `initQueryDataSource` and filter push down private Filter globalTimeFilter; + // parameter of `WITHOUT NULL` clause + private FilterNullParameter filterNullParameter; + + // parameter of `FILL` clause + private FillDescriptor fillDescriptor; + + // parameter of `GROUP BY TIME` clause + private GroupByTimeParameter groupByTimeParameter; + // header of result dataset private DatasetHeader respDatasetHeader; - // e.g. [s1,s2,s3] is query, but [s1, s3] exists in device1, then device1 -> [1, 3], s1 is 1 but - // not 0 because device is the first column - private Map> deviceToMeasurementIndexesMap; - - private boolean finishQueryAfterAnalyze; + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Schema Query Analysis + ///////////////////////////////////////////////////////////////////////////////////////////////// // extra mesaage from config node, used for node management private Set matchedNodes; @@ -168,28 +210,12 @@ public boolean hasDataSource() { || (schemaPartition != null && !schemaPartition.isEmpty()); } - public Map> getSourceExpressions() { - return sourceExpressions; - } - - public void setSourceExpressions(Map> sourceExpressions) { - this.sourceExpressions = sourceExpressions; - } - - public Set getSelectExpressions() { - return selectExpressions; - } - - public void setSelectExpressions(Set selectExpressions) { - this.selectExpressions = selectExpressions; + public boolean isHasRawDataInputAggregation() { + return isHasRawDataInputAggregation; } - public Map> getAggregationExpressions() { - return aggregationExpressions; - } - - public void setAggregationExpressions(Map> aggregationExpressions) { - this.aggregationExpressions = aggregationExpressions; + public void setHasRawDataInputAggregation(boolean hasRawDataInputAggregation) { + isHasRawDataInputAggregation = hasRawDataInputAggregation; } public Map> getGroupByLevelExpressions() { @@ -265,6 +291,89 @@ public Map> getDeviceToMeasurementIndexesMap() { return deviceToMeasurementIndexesMap; } + public Set getSourceExpressions() { + return sourceExpressions; + } + + public void setSourceExpressions(Set sourceExpressions) { + this.sourceExpressions = sourceExpressions; + } + + public Set getAggregationTransformExpressions() { + return aggregationTransformExpressions; + } + + public void setAggregationTransformExpressions(Set aggregationTransformExpressions) { + this.aggregationTransformExpressions = aggregationTransformExpressions; + } + + public Set getAggregationExpressions() { + return aggregationExpressions; + } + + public void setAggregationExpressions(Set aggregationExpressions) { + this.aggregationExpressions = aggregationExpressions; + } + + public Set getTransformExpressions() { + return transformExpressions; + } + + public void setTransformExpressions(Set transformExpressions) { + this.transformExpressions = transformExpressions; + } + + public Map> getDeviceToSourceExpressions() { + return deviceToSourceExpressions; + } + + public void setDeviceToSourceExpressions(Map> deviceToSourceExpressions) { + this.deviceToSourceExpressions = deviceToSourceExpressions; + } + + public Map> getDeviceToAggregationTransformExpressions() { + return deviceToAggregationTransformExpressions; + } + + public void setDeviceToAggregationTransformExpressions( + Map> deviceToAggregationTransformExpressions) { + this.deviceToAggregationTransformExpressions = deviceToAggregationTransformExpressions; + } + + public Map> getDeviceToAggregationExpressions() { + return deviceToAggregationExpressions; + } + + public void setDeviceToAggregationExpressions( + Map> deviceToAggregationExpressions) { + this.deviceToAggregationExpressions = deviceToAggregationExpressions; + } + + public Map> getDeviceToTransformExpressions() { + return deviceToTransformExpressions; + } + + public void setDeviceToTransformExpressions( + Map> deviceToTransformExpressions) { + this.deviceToTransformExpressions = deviceToTransformExpressions; + } + + public boolean isRawDataSource() { + return isRawDataSource; + } + + public void setRawDataSource(boolean rawDataSource) { + isRawDataSource = rawDataSource; + } + + public Map getDeviceToIsRawDataSource() { + return deviceToIsRawDataSource; + } + + public void setDeviceToIsRawDataSource(Map deviceToIsRawDataSource) { + this.deviceToIsRawDataSource = deviceToIsRawDataSource; + } + public Set getMatchedNodes() { return matchedNodes; } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analyzer.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analyzer.java index 83c80399e6ac..a47f4fce44c7 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analyzer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analyzer.java @@ -28,7 +28,6 @@ import org.apache.iotdb.confignode.rpc.thrift.NodeManagementType; import org.apache.iotdb.db.exception.sql.SemanticException; import org.apache.iotdb.db.exception.sql.StatementAnalyzeException; -import org.apache.iotdb.db.metadata.path.MeasurementPath; import org.apache.iotdb.db.mpp.common.MPPQueryContext; import org.apache.iotdb.db.mpp.common.header.ColumnHeader; import org.apache.iotdb.db.mpp.common.header.DatasetHeader; @@ -149,47 +148,53 @@ public Analysis visitQuery(QueryStatement queryStatement, MPPQueryContext contex return analysis; } - List> outputExpressions; - Set selectExpressions = new LinkedHashSet<>(); - Map> sourceExpressions = new HashMap<>(); + // extract global time filter from query filter and determine if there is a value filter + Pair resultPair = analyzeGlobalTimeFilter(queryStatement); + Filter globalTimeFilter = resultPair.left; + boolean hasValueFilter = resultPair.right; + analysis.setGlobalTimeFilter(globalTimeFilter); + analysis.setHasValueFilter(hasValueFilter); + // Example 1: select s1, s1 + s2 as t, udf(udf(s1)) from root.sg.d1 // outputExpressions: [, , // ] - // selectExpressions: [root.sg.d1.s1, root.sg.d1.s1 + root.sg.d1.s2, + // transformExpressions: [root.sg.d1.s1, root.sg.d1.s1 + root.sg.d1.s2, // udf(udf(root.sg.d1.s1))] // sourceExpressions: {root.sg.d1 -> [root.sg.d1.s1, root.sg.d1.s2]} // // Example 2: select s1, s2, s3 as t from root.sg.* align by device // outputExpressions: [, , ] - // selectExpressions: [root.sg.d1.s1, root.sg.d1.s2, root.sg.d1.s3, + // transformExpressions: [root.sg.d1.s1, root.sg.d1.s2, root.sg.d1.s3, // root.sg.d2.s1, root.sg.d2.s2] // sourceExpressions: {root.sg.d1 -> [root.sg.d1.s1, root.sg.d1.s2, root.sg.d1.s2], // root.sg.d2 -> [root.sg.d2.s1, root.sg.d2.s2]} // // Example 3: select sum(s1) + 1 as t, count(s2) from root.sg.d1 // outputExpressions: [, ] - // selectExpressions: [sum(root.sg.d1.s1) + 1, count(root.sg.d1.s2)] + // transformExpressions: [sum(root.sg.d1.s1) + 1, count(root.sg.d1.s2)] // aggregationExpressions: {root.sg.d1 -> [sum(root.sg.d1.s1), count(root.sg.d1.s2)]} // sourceExpressions: {root.sg.d1 -> [sum(root.sg.d1.s1), count(root.sg.d1.s2)]} // // Example 4: select sum(s1) + 1 as t, count(s2) from root.sg.d1 where s1 > 1 // outputExpressions: [, ] - // selectExpressions: [sum(root.sg.d1.s1) + 1, count(root.sg.d1.s2)] + // transformExpressions: [sum(root.sg.d1.s1) + 1, count(root.sg.d1.s2)] // aggregationExpressions: {root.sg.d1 -> [sum(root.sg.d1.s1), count(root.sg.d1.s2)]} // sourceExpressions: {root.sg.d1 -> [root.sg.d1.s1, root.sg.d1.s2]} - List deviceSchemaInfos = new ArrayList<>(); - // a set that contains all measurement names, - Set measurementSet = new HashSet<>(); + List> outputExpressions; if (queryStatement.isAlignByDevice()) { + Map> deviceToTransformExpressions = new HashMap<>(); + + // all selected device + Set deviceList = analyzeFrom(queryStatement, schemaTree); + Map> deviceToMeasurementsMap = new HashMap<>(); outputExpressions = - analyzeFrom( + analyzeSelect( queryStatement, schemaTree, - deviceSchemaInfos, - selectExpressions, - deviceToMeasurementsMap, - measurementSet); + deviceList, + deviceToTransformExpressions, + deviceToMeasurementsMap); Map> deviceToMeasurementIndexesMap = new HashMap<>(); List allMeasurements = @@ -209,67 +214,138 @@ public Analysis visitQuery(QueryStatement queryStatement, MPPQueryContext contex deviceToMeasurementIndexesMap.put(deviceName, indexes); } analysis.setDeviceToMeasurementIndexesMap(deviceToMeasurementIndexesMap); + + Map> deviceToSourceExpressions = new HashMap<>(); + boolean isValueFilterAggregation = queryStatement.isAggregationQuery() && hasValueFilter; + + Map deviceToIsRawDataSource = new HashMap<>(); + + Map> deviceToAggregationExpressions = new HashMap<>(); + Map> deviceToAggregationTransformExpressions = new HashMap<>(); + for (String deviceName : deviceToTransformExpressions.keySet()) { + Set transformExpressions = deviceToTransformExpressions.get(deviceName); + Set aggregationExpressions = new HashSet<>(); + Set aggregationTransformExpressions = new HashSet<>(); + + boolean isHasRawDataInputAggregation = false; + if (queryStatement.isAggregationQuery()) { + // true if nested expressions and UDFs exist in aggregation function + // i.e. select sum(s1 + 1) from root.sg.d1 align by device + isHasRawDataInputAggregation = + analyzeAggregation( + transformExpressions, + aggregationExpressions, + aggregationTransformExpressions); + deviceToAggregationExpressions.put(deviceName, aggregationExpressions); + deviceToAggregationTransformExpressions.put( + deviceName, aggregationTransformExpressions); + } + + boolean isRawDataSource = + !queryStatement.isAggregationQuery() + || isValueFilterAggregation + || isHasRawDataInputAggregation; + + for (Expression expression : transformExpressions) { + updateSource( + expression, + deviceToSourceExpressions.computeIfAbsent( + deviceName, key -> new LinkedHashSet<>()), + isRawDataSource); + } + deviceToIsRawDataSource.put(deviceName, isRawDataSource); + } + analysis.setDeviceToAggregationExpressions(deviceToAggregationExpressions); + analysis.setDeviceToAggregationTransformExpressions( + deviceToAggregationTransformExpressions); + analysis.setDeviceToIsRawDataSource(deviceToIsRawDataSource); + + if (queryStatement.getWhereCondition() != null) { + Map deviceToQueryFilter = new HashMap<>(); + for (PartialPath devicePath : deviceList) { + Expression queryFilter = + analyzeWhereSplitByDevice(queryStatement, devicePath, schemaTree); + deviceToQueryFilter.put(devicePath.getFullPath(), queryFilter); + updateSource( + queryFilter, + deviceToSourceExpressions.computeIfAbsent( + devicePath.getFullPath(), key -> new LinkedHashSet<>()), + true); + } + analysis.setDeviceToQueryFilter(deviceToQueryFilter); + } + analysis.setDeviceToSourceExpressions(deviceToSourceExpressions); + analysis.setDeviceToTransformExpressions(deviceToTransformExpressions); } else { outputExpressions = analyzeSelect(queryStatement, schemaTree); - selectExpressions = - outputExpressions.stream().map(Pair::getLeft).collect(Collectors.toSet()); - } - - if (queryStatement.isGroupByLevel()) { - // map from grouped expression to set of input expressions - Map> groupByLevelExpressions = - analyzeGroupByLevel(queryStatement, outputExpressions, selectExpressions); - analysis.setGroupByLevelExpressions(groupByLevelExpressions); - } + Set transformExpressions = + outputExpressions.stream() + .map(Pair::getLeft) + .collect(Collectors.toCollection(LinkedHashSet::new)); - if (queryStatement.isGroupByTime()) { - analysis.setGroupByTimeParameter( - new GroupByTimeParameter(queryStatement.getGroupByTimeComponent())); - } + if (queryStatement.isGroupByLevel()) { + // map from grouped expression to set of input expressions + Map> groupByLevelExpressions = + analyzeGroupByLevel(queryStatement, outputExpressions, transformExpressions); + analysis.setGroupByLevelExpressions(groupByLevelExpressions); + } - // extract global time filter from query filter and determine if there is a value filter - Pair resultPair = analyzeGlobalTimeFilter(queryStatement); - Filter globalTimeFilter = resultPair.left; - boolean hasValueFilter = resultPair.right; - analysis.setGlobalTimeFilter(globalTimeFilter); - analysis.setHasValueFilter(hasValueFilter); + // true if nested expressions and UDFs exist in aggregation function + // i.e. select sum(s1 + 1) from root.sg.d1 + boolean isHasRawDataInputAggregation = false; + if (queryStatement.isAggregationQuery()) { + Set aggregationExpressions = new HashSet<>(); + Set aggregationTransformExpressions = new HashSet<>(); + isHasRawDataInputAggregation = + analyzeAggregation( + transformExpressions, aggregationExpressions, aggregationTransformExpressions); + analysis.setAggregationExpressions(aggregationExpressions); + analysis.setAggregationTransformExpressions(aggregationTransformExpressions); + } - // generate sourceExpression according to selectExpressions - boolean isValueFilterAggregation = queryStatement.isAggregationQuery() && hasValueFilter; - boolean isRawDataSource = !queryStatement.isAggregationQuery() || isValueFilterAggregation; - for (Expression selectExpr : selectExpressions) { - updateSource(selectExpr, sourceExpressions, isRawDataSource); - } - if (isValueFilterAggregation) { - Map> aggregationExpressions = new HashMap<>(); - for (Expression selectExpr : selectExpressions) { - analyzeAggregation(selectExpr, aggregationExpressions); + // generate sourceExpression according to transformExpressions + Set sourceExpressions = new HashSet<>(); + boolean isValueFilterAggregation = queryStatement.isAggregationQuery() && hasValueFilter; + boolean isRawDataSource = + !queryStatement.isAggregationQuery() + || isValueFilterAggregation + || isHasRawDataInputAggregation; + for (Expression expression : transformExpressions) { + updateSource(expression, sourceExpressions, isRawDataSource); } - analysis.setAggregationExpressions(aggregationExpressions); - } - if (queryStatement.getWhereCondition() != null) { - if (queryStatement.isAlignByDevice()) { - Map deviceToQueryFilter = - analyzeWhereSplitByDevice(queryStatement, deviceSchemaInfos, measurementSet); - deviceToQueryFilter - .values() - .forEach( - queryFilter -> updateSource(queryFilter, sourceExpressions, isRawDataSource)); - analysis.setDeviceToQueryFilter(deviceToQueryFilter); - } else { + if (queryStatement.getWhereCondition() != null) { Expression queryFilter = analyzeWhere(queryStatement, schemaTree); + // update sourceExpression according to queryFilter updateSource(queryFilter, sourceExpressions, isRawDataSource); analysis.setQueryFilter(queryFilter); } + analysis.setRawDataSource(isRawDataSource); + analysis.setSourceExpressions(sourceExpressions); + analysis.setTransformExpressions(transformExpressions); + } + + if (queryStatement.isGroupByTime()) { + analysis.setGroupByTimeParameter( + new GroupByTimeParameter(queryStatement.getGroupByTimeComponent())); } - analysis.setSourceExpressions(sourceExpressions); - analysis.setSelectExpressions(selectExpressions); if (queryStatement.getFilterNullComponent() != null) { - FilterNullParameter filterNullParameter = - analyzeWithoutNull(queryStatement, schemaTree, selectExpressions); + FilterNullParameter filterNullParameter = new FilterNullParameter(); + filterNullParameter.setFilterNullPolicy( + queryStatement.getFilterNullComponent().getWithoutPolicyType()); + List resultFilterNullColumns = new ArrayList<>(); + if (queryStatement.isAlignByDevice()) { + resultFilterNullColumns = + analyzeWithoutNullAlignByDevice( + queryStatement, + outputExpressions.stream().map(Pair::getLeft).collect(Collectors.toSet())); + } else { + resultFilterNullColumns = + analyzeWithoutNull(queryStatement, schemaTree, analysis.getTransformExpressions()); + } + filterNullParameter.setFilterNullColumns(resultFilterNullColumns); analysis.setFilterNullParameter(filterNullParameter); } @@ -295,8 +371,16 @@ public Analysis visitQuery(QueryStatement queryStatement, MPPQueryContext contex analysis.setTypeProvider(typeProvider); // fetch partition information + Set deviceSet = new HashSet<>(); + if (queryStatement.isAlignByDevice()) { + deviceSet = analysis.getDeviceToSourceExpressions().keySet(); + } else { + for (Expression expression : analysis.getSourceExpressions()) { + deviceSet.add(ExpressionAnalyzer.getDeviceNameInSourceExpression(expression)); + } + } Map> sgNameToQueryParamsMap = new HashMap<>(); - for (String devicePath : sourceExpressions.keySet()) { + for (String devicePath : deviceSet) { DataPartitionQueryParam queryParam = new DataPartitionQueryParam(); queryParam.setDevicePath(devicePath); sgNameToQueryParamsMap @@ -356,184 +440,111 @@ private List> analyzeSelect( return outputExpressions; } - private List> analyzeFrom( - QueryStatement queryStatement, - SchemaTree schemaTree, - List allDeviceSchemaInfos, - Set selectExpressions, - Map> deviceToMeasurementsMap, - Set measurementSet) { + private Set analyzeFrom(QueryStatement queryStatement, SchemaTree schemaTree) { // device path patterns in FROM clause List devicePatternList = queryStatement.getFromComponent().getPrefixPaths(); - // a list of measurement name with alias (null if alias not exist) - List> measurementWithAliasList = - getAllMeasurements(queryStatement, measurementSet); - - // a list contains all selected paths - List allSelectedPaths = new ArrayList<>(); + Set deviceList = new LinkedHashSet<>(); for (PartialPath devicePattern : devicePatternList) { - // get matched device path and all measurement schema infos under this device - List deviceSchemaInfos = schemaTree.getMatchedDevices(devicePattern); - allDeviceSchemaInfos.addAll(deviceSchemaInfos); - for (DeviceSchemaInfo deviceSchema : deviceSchemaInfos) { - // add matched path into allSelectedPaths - allSelectedPaths.addAll(deviceSchema.getMeasurements(measurementSet)); - } - } - - // convert allSelectedPaths to a map from measurement name to corresponding paths - Map> measurementNameToPathsMap = new HashMap<>(); - Map aliasToMeasurementNameMap = new HashMap<>(); - for (MeasurementPath measurementPath : allSelectedPaths) { - measurementNameToPathsMap - .computeIfAbsent(measurementPath.getMeasurement(), key -> new ArrayList<>()) - .add(measurementPath); - if (measurementPath.isMeasurementAliasExists()) { - if (aliasToMeasurementNameMap.containsKey(measurementPath.getMeasurementAlias()) - && !Objects.equals( - aliasToMeasurementNameMap.get(measurementPath.getMeasurementAlias()), - measurementPath.getMeasurement())) { - throw new SemanticException( - String.format( - "ALIGN BY DEVICE: alias '%s' can only be matched with one measurement", - measurementPath.getMeasurementAlias())); - } - aliasToMeasurementNameMap.put( - measurementPath.getMeasurementAlias(), measurementPath.getMeasurement()); - } + // get all matched devices + deviceList.addAll( + schemaTree.getMatchedDevices(devicePattern).stream() + .map(DeviceSchemaInfo::getDevicePath) + .collect(Collectors.toList())); } - // check whether the datatype of paths which has the same measurement name are consistent - // if not, throw a SemanticException - measurementNameToPathsMap.values().forEach(this::checkDataTypeConsistencyInAlignByDevice); + return deviceList; + } - // apply SLIMIT & SOFFSET and set outputExpressions & selectExpressions + private List> analyzeSelect( + QueryStatement queryStatement, + SchemaTree schemaTree, + Set deviceList, + Map> deviceToTransformExpressions, + Map> deviceToMeasurementsMap) { List> outputExpressions = new ArrayList<>(); ColumnPaginationController paginationController = new ColumnPaginationController( queryStatement.getSeriesLimit(), queryStatement.getSeriesOffset(), false); - for (Pair measurementAliasPair : measurementWithAliasList) { - String measurement = - ExpressionAnalyzer.getPathInSourceExpression(measurementAliasPair.left).toString(); - if (measurementNameToPathsMap.containsKey(measurement)) { - List measurementPaths = measurementNameToPathsMap.get(measurement); - measurementPaths.forEach(MeasurementPath::removeMeasurementAlias); - TSDataType dataType = measurementPaths.get(0).getSeriesType(); - if (paginationController.hasCurOffset()) { - paginationController.consumeOffset(); - continue; - } - if (paginationController.hasCurLimit()) { - outputExpressions.add(measurementAliasPair); - typeProvider.setType(measurementAliasPair.left.getExpressionString(), dataType); - for (MeasurementPath measurementPath : measurementPaths) { - Expression tmpExpression = - ExpressionAnalyzer.replacePathInSourceExpression( - measurementAliasPair.left, measurementPath); - typeProvider.setType(tmpExpression.getExpressionString(), dataType); - selectExpressions.add(tmpExpression); - deviceToMeasurementsMap - .computeIfAbsent(measurementPath.getDevice(), key -> new LinkedHashSet<>()) - .add(measurementAliasPair.left.getExpressionString()); - } - paginationController.consumeLimit(); - } else { - break; + + for (ResultColumn resultColumn : queryStatement.getSelectComponent().getResultColumns()) { + Expression selectExpression = resultColumn.getExpression(); + boolean hasAlias = resultColumn.hasAlias(); + + // measurement expression after removing wildcard + // use LinkedHashMap for order-preserving + Map> measurementToDeviceTransformExpressions = + new LinkedHashMap<>(); + for (PartialPath device : deviceList) { + List transformExpressions = + ExpressionAnalyzer.concatDeviceAndRemoveWildcard( + selectExpression, device, schemaTree, typeProvider); + for (Expression transformExpression : transformExpressions) { + measurementToDeviceTransformExpressions + .computeIfAbsent( + ExpressionAnalyzer.getMeasurementExpression(transformExpression), + key -> new LinkedHashMap<>()) + .put( + device.getFullPath(), + ExpressionAnalyzer.removeAliasFromExpression(transformExpression)); } - } else if (aliasToMeasurementNameMap.containsKey(measurement) - && measurementNameToPathsMap.containsKey(aliasToMeasurementNameMap.get(measurement))) { - List measurementPaths = - measurementNameToPathsMap.get(aliasToMeasurementNameMap.get(measurement)); - measurementPaths.forEach(MeasurementPath::removeMeasurementAlias); - TSDataType dataType = measurementPaths.get(0).getSeriesType(); - Expression expressionWithAlias = measurementAliasPair.left; - Expression expressionWithoutAlias = - ExpressionAnalyzer.removeAliasInMeasurementExpression( - measurementAliasPair.left, aliasToMeasurementNameMap); - String alias = - measurementAliasPair.right != null - ? measurementAliasPair.right - : expressionWithAlias.getExpressionString(); + } + + if (hasAlias && measurementToDeviceTransformExpressions.keySet().size() > 1) { + throw new SemanticException( + String.format( + "alias '%s' can only be matched with one time series", resultColumn.getAlias())); + } + + for (Expression measurementExpression : measurementToDeviceTransformExpressions.keySet()) { if (paginationController.hasCurOffset()) { paginationController.consumeOffset(); continue; } if (paginationController.hasCurLimit()) { - outputExpressions.add(new Pair<>(expressionWithoutAlias, alias)); - typeProvider.setType(expressionWithoutAlias.getExpressionString(), dataType); - for (MeasurementPath measurementPath : measurementPaths) { - Expression tmpExpression = - ExpressionAnalyzer.replacePathInSourceExpression( - expressionWithoutAlias, measurementPath); - typeProvider.setType(tmpExpression.getExpressionString(), dataType); - selectExpressions.add(tmpExpression); + Map deviceToTransformExpressionOfOneMeasurement = + measurementToDeviceTransformExpressions.get(measurementExpression); + deviceToTransformExpressionOfOneMeasurement + .values() + .forEach(expression -> expression.inferTypes(typeProvider)); + // check whether the datatype of paths which has the same measurement name are + // consistent + // if not, throw a SemanticException + checkDataTypeConsistencyInAlignByDevice( + new ArrayList<>(deviceToTransformExpressionOfOneMeasurement.values())); + + // add outputExpressions + Expression measurementExpressionWithoutAlias = + ExpressionAnalyzer.removeAliasFromExpression(measurementExpression); + String alias = + !Objects.equals(measurementExpressionWithoutAlias, measurementExpression) + ? measurementExpression.getExpressionString() + : null; + alias = hasAlias ? resultColumn.getAlias() : alias; + ExpressionAnalyzer.updateTypeProvider(measurementExpressionWithoutAlias, typeProvider); + measurementExpressionWithoutAlias.inferTypes(typeProvider); + outputExpressions.add(new Pair<>(measurementExpressionWithoutAlias, alias)); + + // add deviceToTransformExpressions + for (String deviceName : deviceToTransformExpressionOfOneMeasurement.keySet()) { + Expression transformExpression = + deviceToTransformExpressionOfOneMeasurement.get(deviceName); + ExpressionAnalyzer.updateTypeProvider(transformExpression, typeProvider); + transformExpression.inferTypes(typeProvider); + deviceToTransformExpressions + .computeIfAbsent(deviceName, key -> new LinkedHashSet<>()) + .add(ExpressionAnalyzer.removeAliasFromExpression(transformExpression)); deviceToMeasurementsMap - .computeIfAbsent(measurementPath.getDevice(), key -> new LinkedHashSet<>()) - .add(expressionWithoutAlias.getExpressionString()); + .computeIfAbsent(deviceName, key -> new LinkedHashSet<>()) + .add(measurementExpressionWithoutAlias.getExpressionString()); } paginationController.consumeLimit(); } else { break; } - } else if (measurement.equals(IoTDBConstant.ONE_LEVEL_PATH_WILDCARD)) { - for (String measurementName : measurementNameToPathsMap.keySet()) { - List measurementPaths = measurementNameToPathsMap.get(measurementName); - measurementPaths.forEach(MeasurementPath::removeMeasurementAlias); - TSDataType dataType = measurementPaths.get(0).getSeriesType(); - if (paginationController.hasCurOffset()) { - paginationController.consumeOffset(); - continue; - } - if (paginationController.hasCurLimit()) { - // replace `*` with exact measurement name - Expression replacedMeasurement = - ExpressionAnalyzer.replacePathInSourceExpression( - measurementAliasPair.left, measurementName); - typeProvider.setType(replacedMeasurement.getExpressionString(), dataType); - outputExpressions.add(new Pair<>(replacedMeasurement, measurementAliasPair.right)); - for (MeasurementPath measurementPath : measurementPaths) { - // replace `*` with exact measurement path - Expression tmpExpression = - ExpressionAnalyzer.replacePathInSourceExpression( - measurementAliasPair.left, measurementPath); - typeProvider.setType(tmpExpression.getExpressionString(), dataType); - selectExpressions.add(tmpExpression); - deviceToMeasurementsMap - .computeIfAbsent(measurementPath.getDevice(), key -> new LinkedHashSet<>()) - .add(replacedMeasurement.getExpressionString()); - } - paginationController.consumeLimit(); - } else { - break; - } - } - if (!paginationController.hasCurLimit()) { - break; - } - } else { - // do nothing or warning } } - return outputExpressions; - } - private List> getAllMeasurements( - QueryStatement queryStatement, Set measurementSet) { - List> measurementWithAliasList = - queryStatement.getSelectComponent().getResultColumns().stream() - .map( - resultColumn -> - ExpressionAnalyzer.getMeasurementWithAliasInSourceExpression( - resultColumn.getExpression(), resultColumn.getAlias())) - .collect(Collectors.toList()); - measurementSet.addAll( - measurementWithAliasList.stream() - .map(Pair::getLeft) - .map(ExpressionAnalyzer::collectPaths) - .flatMap(Set::stream) - .map(PartialPath::getFullPath) - .collect(Collectors.toSet())); - return measurementWithAliasList; + return outputExpressions; } private Pair analyzeGlobalTimeFilter(QueryStatement queryStatement) { @@ -565,29 +576,32 @@ private Pair analyzeGlobalTimeFilter(QueryStatement queryStatem } private void updateSource( - Expression selectExpr, - Map> sourceExpressions, - boolean isRawDataSource) { - for (Expression sourceExpression : - ExpressionAnalyzer.searchSourceExpressions(selectExpr, isRawDataSource)) { - sourceExpressions - .computeIfAbsent( - ExpressionAnalyzer.getDeviceNameInSourceExpression(sourceExpression), - key -> new LinkedHashSet<>()) - .add(sourceExpression); - } + Expression selectExpr, Set sourceExpressions, boolean isRawDataSource) { + sourceExpressions.addAll( + ExpressionAnalyzer.searchSourceExpressions(selectExpr, isRawDataSource)); } - private void analyzeAggregation( - Expression selectExpr, Map> aggregationExpressions) { - for (Expression aggregationExpression : - ExpressionAnalyzer.searchAggregationExpressions(selectExpr)) { - aggregationExpressions - .computeIfAbsent( - ExpressionAnalyzer.getDeviceNameInSourceExpression(aggregationExpression), - key -> new HashSet<>()) - .add(aggregationExpression); + private boolean analyzeAggregation( + Set transformExpressions, + Set aggregationExpressions, + Set aggregationTransformExpressions) { + // true if nested expressions and UDFs exist in aggregation function + // i.e. select sum(s1 + 1) from root.sg.d1 align by device + boolean isHasRawDataInputAggregation = false; + for (Expression expression : transformExpressions) { + for (Expression aggregationExpression : + ExpressionAnalyzer.searchAggregationExpressions(expression)) { + aggregationExpressions.add(aggregationExpression); + aggregationTransformExpressions.addAll(aggregationExpression.getExpressions()); + } } + for (Expression aggregationTransformExpression : aggregationTransformExpressions) { + if (ExpressionAnalyzer.checkIsNeedTransform(aggregationTransformExpression)) { + isHasRawDataInputAggregation = true; + break; + } + } + return isHasRawDataInputAggregation; } private Expression analyzeWhere(QueryStatement queryStatement, SchemaTree schemaTree) { @@ -601,30 +615,22 @@ private Expression analyzeWhere(QueryStatement queryStatement, SchemaTree schema rewrittenPredicates.stream().distinct().collect(Collectors.toList())); } - private Map analyzeWhereSplitByDevice( - QueryStatement queryStatement, - List deviceSchemaInfos, - Set measurementSet) { - Map deviceToQueryFilter = new HashMap<>(); - for (DeviceSchemaInfo deviceSchemaInfo : deviceSchemaInfos) { - List rewrittenPredicates = - ExpressionAnalyzer.removeWildcardInQueryFilterByDevice( - queryStatement.getWhereCondition().getPredicate(), - deviceSchemaInfo, - measurementSet, - typeProvider); - deviceToQueryFilter.put( - deviceSchemaInfo.getDevicePath().getFullPath(), - ExpressionUtils.constructQueryFilter( - rewrittenPredicates.stream().distinct().collect(Collectors.toList()))); - } - return deviceToQueryFilter; + private Expression analyzeWhereSplitByDevice( + QueryStatement queryStatement, PartialPath devicePath, SchemaTree schemaTree) { + List rewrittenPredicates = + ExpressionAnalyzer.removeWildcardInQueryFilterByDevice( + queryStatement.getWhereCondition().getPredicate(), + devicePath, + schemaTree, + typeProvider); + return ExpressionUtils.constructQueryFilter( + rewrittenPredicates.stream().distinct().collect(Collectors.toList())); } private Map> analyzeGroupByLevel( QueryStatement queryStatement, List> outputExpressions, - Set selectExpressions) { + Set transformExpressions) { GroupByLevelController groupByLevelController = new GroupByLevelController(queryStatement.getGroupByLevelComponent().getLevels()); for (Pair measurementWithAlias : outputExpressions) { @@ -654,7 +660,7 @@ private Map> analyzeGroupByLevel( } } - // reset outputExpressions & selectExpressions after applying SLIMIT/SOFFSET + // reset outputExpressions & transformExpressions after applying SLIMIT/SOFFSET outputExpressions.clear(); for (Expression groupedExpression : groupByLevelExpressions.keySet()) { TSDataType dataType = @@ -668,42 +674,68 @@ private Map> analyzeGroupByLevel( groupedExpression, groupByLevelController.getAlias(groupedExpression.getExpressionString()))); } - selectExpressions.clear(); - selectExpressions.addAll( + transformExpressions.clear(); + transformExpressions.addAll( groupByLevelExpressions.values().stream() .flatMap(Set::stream) .collect(Collectors.toSet())); return groupByLevelExpressions; } - private FilterNullParameter analyzeWithoutNull( - QueryStatement queryStatement, SchemaTree schemaTree, Set selectExpressions) { - FilterNullParameter filterNullParameter = new FilterNullParameter(); - filterNullParameter.setFilterNullPolicy( - queryStatement.getFilterNullComponent().getWithoutPolicyType()); + private List analyzeWithoutNullAlignByDevice( + QueryStatement queryStatement, Set outputExpressions) { List resultFilterNullColumns = new ArrayList<>(); List rawFilterNullColumns = queryStatement.getFilterNullComponent().getWithoutNullColumns(); + + // don't specify columns, by default, it is effective for all columns + if (rawFilterNullColumns.isEmpty()) { + resultFilterNullColumns.addAll(outputExpressions); + return resultFilterNullColumns; + } + + for (Expression filterNullColumn : rawFilterNullColumns) { + if (!outputExpressions.contains(filterNullColumn)) { + throw new SemanticException( + String.format( + "The without null column '%s' don't match the columns queried.", + filterNullColumn)); + } + resultFilterNullColumns.add(filterNullColumn); + } + return resultFilterNullColumns; + } + + private List analyzeWithoutNull( + QueryStatement queryStatement, + SchemaTree schemaTree, + Set transformExpressions) { + List resultFilterNullColumns = new ArrayList<>(); + List rawFilterNullColumns = + queryStatement.getFilterNullComponent().getWithoutNullColumns(); + + // don't specify columns, by default, it is effective for all columns + if (rawFilterNullColumns.isEmpty()) { + resultFilterNullColumns.addAll(transformExpressions); + return resultFilterNullColumns; + } + for (Expression filterNullColumn : rawFilterNullColumns) { List resultExpressions = ExpressionAnalyzer.removeWildcardInExpression(filterNullColumn, schemaTree); for (Expression expression : resultExpressions) { Expression expressionWithoutAlias = ExpressionAnalyzer.removeAliasFromExpression(expression); - if (!selectExpressions.contains(expressionWithoutAlias)) { + if (!transformExpressions.contains(expressionWithoutAlias)) { throw new SemanticException( String.format( - "The without null column '%s' don't match the columns queried.", expression)); + "The without null column '%s' don't match the columns queried.", + filterNullColumn)); } resultFilterNullColumns.add(expressionWithoutAlias); } } - // don't specify columns, by default, it is effective for all columns - if (rawFilterNullColumns.isEmpty()) { - resultFilterNullColumns.addAll(selectExpressions); - } - filterNullParameter.setFilterNullColumns(resultFilterNullColumns); - return filterNullParameter; + return resultFilterNullColumns; } private DatasetHeader analyzeOutput( @@ -733,10 +765,10 @@ private DatasetHeader analyzeOutput( *

an inconsistent example: select s0 from root.sg1.d1, root.sg1.d2 align by device, return * false while root.sg1.d1.s0 is INT32 and root.sg1.d2.s0 is FLOAT. */ - private void checkDataTypeConsistencyInAlignByDevice(List measurementPaths) { - TSDataType checkedDataType = measurementPaths.get(0).getSeriesType(); - for (MeasurementPath path : measurementPaths) { - if (path.getSeriesType() != checkedDataType) { + private void checkDataTypeConsistencyInAlignByDevice(List expressions) { + TSDataType checkedDataType = typeProvider.getType(expressions.get(0).getExpressionString()); + for (Expression expression : expressions) { + if (typeProvider.getType(expression.getExpressionString()) != checkedDataType) { throw new SemanticException( "ALIGN BY DEVICE: the data types of the same measurement column should be the same across devices."); } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ConcatPathRewriter.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ConcatPathRewriter.java index 0c7a8c7e9881..f38351a50cd6 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ConcatPathRewriter.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ConcatPathRewriter.java @@ -54,26 +54,48 @@ public Statement rewrite(Statement statement, PathPatternTree patternTree) // prefix paths in the FROM clause List prefixPaths = queryStatement.getFromComponent().getPrefixPaths(); - // concat SELECT with FROM - List resultColumns = - concatSelectWithFrom( - queryStatement.getSelectComponent(), prefixPaths, queryStatement.isGroupByLevel()); - queryStatement.getSelectComponent().setResultColumns(resultColumns); + if (queryStatement.isAlignByDevice()) { + queryStatement.getSelectComponent().getResultColumns().stream() + .map(ResultColumn::getExpression) + .forEach( + expression -> + ExpressionAnalyzer.constructPatternTreeFromExpression( + expression, prefixPaths, patternTree)); + } else { + // concat SELECT with FROM + List resultColumns = + concatSelectWithFrom( + queryStatement.getSelectComponent(), prefixPaths, queryStatement.isGroupByLevel()); + queryStatement.getSelectComponent().setResultColumns(resultColumns); + } // concat WITHOUT NULL with FROM if (queryStatement.getFilterNullComponent() != null && !queryStatement.getFilterNullComponent().getWithoutNullColumns().isEmpty()) { - List withoutNullColumns = - concatWithoutNullColumnsWithFrom( - queryStatement.getFilterNullComponent(), - prefixPaths, - queryStatement.getSelectComponent().getAliasToColumnMap()); - queryStatement.getFilterNullComponent().setWithoutNullColumns(withoutNullColumns); + FilterNullComponent filterNullComponent = queryStatement.getFilterNullComponent(); + Map aliasToColumnMap = + queryStatement.getSelectComponent().getAliasToColumnMap(); + + // replace alias + List replacedWithoutNullColumns = + filterNullComponent.getWithoutNullColumns().stream() + .map( + expression -> + aliasToColumnMap.getOrDefault(expression.getExpressionString(), expression)) + .collect(Collectors.toList()); + + if (queryStatement.isAlignByDevice()) { + queryStatement.getFilterNullComponent().setWithoutNullColumns(replacedWithoutNullColumns); + } else { + List withoutNullColumns = + concatWithoutNullColumnsWithFrom(replacedWithoutNullColumns, prefixPaths); + queryStatement.getFilterNullComponent().setWithoutNullColumns(withoutNullColumns); + } } // concat WHERE with FROM if (queryStatement.getWhereCondition() != null) { - ExpressionAnalyzer.constructPatternTreeFromQueryFilter( + ExpressionAnalyzer.constructPatternTreeFromExpression( queryStatement.getWhereCondition().getPredicate(), prefixPaths, patternTree); } return queryStatement; @@ -100,7 +122,10 @@ private List concatSelectWithFrom( } resultColumns.addAll( resultExpressions.stream() - .map(expression -> new ResultColumn(expression, resultColumn.getAlias())) + .map( + expression -> + new ResultColumn( + expression, resultColumn.getAlias(), resultColumn.getColumnType())) .collect(Collectors.toList())); } return resultColumns; @@ -111,20 +136,10 @@ private List concatSelectWithFrom( * full path pattern. And construct pattern tree. */ private List concatWithoutNullColumnsWithFrom( - FilterNullComponent filterNullComponent, - List prefixPaths, - Map aliasToColumnMap) + List withoutNullColumns, List prefixPaths) throws StatementAnalyzeException { - // raw expression after replace alias - List rawWithoutNullColumns = - filterNullComponent.getWithoutNullColumns().stream() - .map( - expression -> - aliasToColumnMap.getOrDefault(expression.getExpressionString(), expression)) - .collect(Collectors.toList()); - // result after concat - return rawWithoutNullColumns.stream() + return withoutNullColumns.stream() .map( expression -> ExpressionAnalyzer.concatExpressionWithSuffixPaths( diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ExpressionAnalyzer.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ExpressionAnalyzer.java index 0578ce582e88..104545e7fea9 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ExpressionAnalyzer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ExpressionAnalyzer.java @@ -20,13 +20,12 @@ package org.apache.iotdb.db.mpp.plan.analyze; import org.apache.iotdb.commons.conf.IoTDBConstant; -import org.apache.iotdb.commons.exception.IllegalPathException; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.exception.sql.SemanticException; import org.apache.iotdb.db.metadata.path.MeasurementPath; -import org.apache.iotdb.db.mpp.common.schematree.DeviceSchemaInfo; import org.apache.iotdb.db.mpp.common.schematree.PathPatternTree; import org.apache.iotdb.db.mpp.common.schematree.SchemaTree; +import org.apache.iotdb.db.mpp.plan.statement.component.ResultColumn; import org.apache.iotdb.db.qp.constant.SQLConstant; import org.apache.iotdb.db.query.expression.Expression; import org.apache.iotdb.db.query.expression.ExpressionType; @@ -47,22 +46,15 @@ import org.apache.iotdb.db.query.expression.unary.InExpression; import org.apache.iotdb.db.query.expression.unary.LogicNotExpression; import org.apache.iotdb.db.query.expression.unary.UnaryExpression; -import org.apache.iotdb.db.utils.SchemaUtils; import org.apache.iotdb.tsfile.common.constant.TsFileConstant; import org.apache.iotdb.tsfile.read.filter.TimeFilter; import org.apache.iotdb.tsfile.read.filter.basic.Filter; import org.apache.iotdb.tsfile.read.filter.factory.FilterFactory; import org.apache.iotdb.tsfile.utils.Pair; -import com.google.common.collect.Sets; -import org.apache.commons.lang.Validate; - import java.util.ArrayList; import java.util.Collections; -import java.util.HashSet; import java.util.List; -import java.util.Map; -import java.util.Set; import java.util.stream.Collectors; import static org.apache.iotdb.db.mpp.plan.analyze.ExpressionUtils.cartesianProduct; @@ -106,28 +98,49 @@ public static void checkIsAllMeasurement(Expression expression) { } /** - * Check if expression is a built-in aggregation function. If not, throw a {@link - * SemanticException}. + * Identify the expression is a valid built-in aggregation function. * * @param expression expression to be checked + * @return true if this expression is valid */ - public static void checkIsAllAggregation(Expression expression) { + public static ResultColumn.ColumnType identifyOutputColumnType(Expression expression) { if (expression instanceof BinaryExpression) { - checkIsAllAggregation(((BinaryExpression) expression).getLeftExpression()); - checkIsAllAggregation(((BinaryExpression) expression).getRightExpression()); + ResultColumn.ColumnType leftType = + identifyOutputColumnType(((BinaryExpression) expression).getLeftExpression()); + ResultColumn.ColumnType rightType = + identifyOutputColumnType(((BinaryExpression) expression).getRightExpression()); + if ((leftType == ResultColumn.ColumnType.RAW + && rightType == ResultColumn.ColumnType.AGGREGATION) + || (leftType == ResultColumn.ColumnType.AGGREGATION + && rightType == ResultColumn.ColumnType.RAW)) { + throw new SemanticException( + "Raw data and aggregation result hybrid calculation is not supported."); + } + if (leftType == ResultColumn.ColumnType.CONSTANT + && rightType == ResultColumn.ColumnType.CONSTANT) { + throw new SemanticException("Constant column is not supported."); + } + if (leftType != ResultColumn.ColumnType.CONSTANT) { + return leftType; + } + return rightType; } else if (expression instanceof UnaryExpression) { - checkIsAllAggregation(((UnaryExpression) expression).getExpression()); + return identifyOutputColumnType(((UnaryExpression) expression).getExpression()); } else if (expression instanceof FunctionExpression) { - if (expression.getExpressions().size() != 1 - || !(expression.getExpressions().get(0) instanceof TimeSeriesOperand)) { - throw new SemanticException( - "The argument of the aggregation function must be a time series."); + if (!expression.isBuiltInAggregationFunctionExpression()) { + return ResultColumn.ColumnType.RAW; } - } else if (expression instanceof TimeSeriesOperand) { - throw new SemanticException( - "Raw data queries and aggregated queries are not allowed to appear at the same time."); - } else if (expression instanceof TimestampOperand || expression instanceof ConstantOperand) { - // do nothing + for (Expression childExpression : expression.getExpressions()) { + if (identifyOutputColumnType(childExpression) == ResultColumn.ColumnType.AGGREGATION) { + throw new SemanticException( + "Aggregation results cannot be as input of the aggregation function."); + } + } + return ResultColumn.ColumnType.AGGREGATION; + } else if (expression instanceof TimeSeriesOperand || expression instanceof TimestampOperand) { + return ResultColumn.ColumnType.RAW; + } else if (expression instanceof ConstantOperand) { + return ResultColumn.ColumnType.CONSTANT; } else { throw new IllegalArgumentException( "unsupported expression type: " + expression.getExpressionType()); @@ -199,19 +212,19 @@ public static List concatExpressionWithSuffixPaths( * @param prefixPaths prefix paths in the FROM clause * @param patternTree a PathPatternTree contains all paths to query */ - public static void constructPatternTreeFromQueryFilter( + public static void constructPatternTreeFromExpression( Expression predicate, List prefixPaths, PathPatternTree patternTree) { if (predicate instanceof BinaryExpression) { - constructPatternTreeFromQueryFilter( + constructPatternTreeFromExpression( ((BinaryExpression) predicate).getLeftExpression(), prefixPaths, patternTree); - constructPatternTreeFromQueryFilter( + constructPatternTreeFromExpression( ((BinaryExpression) predicate).getRightExpression(), prefixPaths, patternTree); } else if (predicate instanceof UnaryExpression) { - constructPatternTreeFromQueryFilter( + constructPatternTreeFromExpression( ((UnaryExpression) predicate).getExpression(), prefixPaths, patternTree); } else if (predicate instanceof FunctionExpression) { for (Expression suffixExpression : predicate.getExpressions()) { - constructPatternTreeFromQueryFilter(suffixExpression, prefixPaths, patternTree); + constructPatternTreeFromExpression(suffixExpression, prefixPaths, patternTree); } } else if (predicate instanceof TimeSeriesOperand) { PartialPath rawPath = ((TimeSeriesOperand) predicate).getPath(); @@ -371,32 +384,90 @@ public static List removeWildcardInQueryFilter( } } + /** + * Concat expression with the device path in the FROM clause.And then, bind schema ({@link + * PartialPath} -> {@link MeasurementPath}) and removes wildcards in Expression. This method used + * in ALIGN BY DEVICE query. + * + * @param devicePath device path in the FROM clause + * @return expression list with full path and after binding schema + */ + public static List concatDeviceAndRemoveWildcard( + Expression expression, + PartialPath devicePath, + SchemaTree schemaTree, + TypeProvider typeProvider) { + if (expression instanceof BinaryExpression) { + List leftExpressions = + concatDeviceAndRemoveWildcard( + ((BinaryExpression) expression).getLeftExpression(), + devicePath, + schemaTree, + typeProvider); + List rightExpressions = + concatDeviceAndRemoveWildcard( + ((BinaryExpression) expression).getRightExpression(), + devicePath, + schemaTree, + typeProvider); + return reconstructBinaryExpressions( + expression.getExpressionType(), leftExpressions, rightExpressions); + } else if (expression instanceof UnaryExpression) { + List childExpressions = + concatDeviceAndRemoveWildcard( + ((UnaryExpression) expression).getExpression(), devicePath, schemaTree, typeProvider); + return reconstructUnaryExpressions((UnaryExpression) expression, childExpressions); + } else if (expression instanceof FunctionExpression) { + List> extendedExpressions = new ArrayList<>(); + for (Expression suffixExpression : expression.getExpressions()) { + extendedExpressions.add( + concatDeviceAndRemoveWildcard(suffixExpression, devicePath, schemaTree, typeProvider)); + } + List> childExpressionsList = new ArrayList<>(); + cartesianProduct(extendedExpressions, childExpressionsList, 0, new ArrayList<>()); + return reconstructFunctionExpressions((FunctionExpression) expression, childExpressionsList); + } else if (expression instanceof TimeSeriesOperand) { + PartialPath measurement = ((TimeSeriesOperand) expression).getPath(); + PartialPath concatPath = devicePath.concatPath(measurement); + + List actualPaths = schemaTree.searchMeasurementPaths(concatPath).left; + List noStarPaths = new ArrayList<>(actualPaths); + noStarPaths.forEach(path -> typeProvider.setType(path.getFullPath(), path.getSeriesType())); + return reconstructTimeSeriesOperands(noStarPaths); + } else if (expression instanceof TimestampOperand) { + // do nothing in the case of "where time > 5" + return Collections.singletonList(expression); + } else if (expression instanceof ConstantOperand) { + return Collections.singletonList(expression); + } else { + throw new IllegalArgumentException( + "unsupported expression type: " + expression.getExpressionType()); + } + } + /** * Concat measurement in WHERE clause with device path. And then, bind schema ({@link PartialPath} * -> {@link MeasurementPath}) and removes wildcards. * - * @param deviceSchemaInfo device path and schema infos of measurements under this device - * @param measurementSet - * @param typeProvider a map to record output symbols and their data types * @return the expression list with full path and after binding schema */ public static List removeWildcardInQueryFilterByDevice( Expression predicate, - DeviceSchemaInfo deviceSchemaInfo, - Set measurementSet, + PartialPath devicePath, + SchemaTree schemaTree, TypeProvider typeProvider) { if (predicate instanceof BinaryExpression) { List leftExpressions = removeWildcardInQueryFilterByDevice( ((BinaryExpression) predicate).getLeftExpression(), - deviceSchemaInfo, - measurementSet, + devicePath, + schemaTree, typeProvider); List rightExpressions = removeWildcardInQueryFilterByDevice( ((BinaryExpression) predicate).getRightExpression(), - deviceSchemaInfo, - measurementSet, + devicePath, + schemaTree, typeProvider); if (predicate.getExpressionType() == ExpressionType.LOGIC_AND) { List resultExpressions = new ArrayList<>(leftExpressions); @@ -408,10 +479,7 @@ public static List removeWildcardInQueryFilterByDevice( } else if (predicate instanceof UnaryExpression) { List childExpressions = removeWildcardInQueryFilterByDevice( - ((UnaryExpression) predicate).getExpression(), - deviceSchemaInfo, - measurementSet, - typeProvider); + ((UnaryExpression) predicate).getExpression(), devicePath, schemaTree, typeProvider); return reconstructUnaryExpressions((UnaryExpression) predicate, childExpressions); } else if (predicate instanceof FunctionExpression) { if (predicate.isBuiltInAggregationFunctionExpression()) { @@ -421,29 +489,25 @@ public static List removeWildcardInQueryFilterByDevice( for (Expression suffixExpression : predicate.getExpressions()) { extendedExpressions.add( removeWildcardInQueryFilterByDevice( - suffixExpression, deviceSchemaInfo, measurementSet, typeProvider)); + suffixExpression, devicePath, schemaTree, typeProvider)); } List> childExpressionsList = new ArrayList<>(); cartesianProduct(extendedExpressions, childExpressionsList, 0, new ArrayList<>()); return reconstructFunctionExpressions((FunctionExpression) predicate, childExpressionsList); } else if (predicate instanceof TimeSeriesOperand) { - PartialPath filterPath = ((TimeSeriesOperand) predicate).getPath(); - String measurement = filterPath.getFullPath(); - List concatPaths = new ArrayList<>(); - if (measurement.equals(IoTDBConstant.ONE_LEVEL_PATH_WILDCARD)) { - concatPaths.addAll(deviceSchemaInfo.getMeasurements(measurementSet)); - } else { - MeasurementPath concatPath = deviceSchemaInfo.getPathByMeasurement(measurement); - if (concatPath == null) { - throw new SemanticException( - String.format( - "ALIGN BY DEVICE: measurement '%s' does not exist in device '%s'", - measurement, deviceSchemaInfo.getDevicePath())); - } - concatPaths.add(concatPath); + PartialPath measurement = ((TimeSeriesOperand) predicate).getPath(); + PartialPath concatPath = devicePath.concatPath(measurement); + + List noStarPaths = schemaTree.searchMeasurementPaths(concatPath).left; + if (noStarPaths.size() == 0) { + throw new SemanticException( + String.format( + "ALIGN BY DEVICE: measurement '%s' does not exist in device '%s'", + measurement, devicePath)); } - concatPaths.forEach(path -> typeProvider.setType(path.getFullPath(), path.getSeriesType())); - return reconstructTimeSeriesOperands(concatPaths); + + noStarPaths.forEach(path -> typeProvider.setType(path.getFullPath(), path.getSeriesType())); + return reconstructTimeSeriesOperands(noStarPaths); } else if (predicate instanceof TimestampOperand) { // do nothing in the case of "where time > 5" return Collections.singletonList(predicate); @@ -590,34 +654,17 @@ public static List searchAggregationExpressions(Expression expressio } else if (expression instanceof UnaryExpression) { return searchAggregationExpressions(((UnaryExpression) expression).getExpression()); } else if (expression instanceof FunctionExpression) { - return Collections.singletonList(expression); - } else if (expression instanceof LeafOperand) { - return Collections.emptyList(); - } else { - throw new IllegalArgumentException( - "unsupported expression type: " + expression.getExpressionType()); - } - } + if (expression.isBuiltInAggregationFunctionExpression()) { + return Collections.singletonList(expression); + } - /** Returns all the timeseries path in the expression */ - public static Set collectPaths(Expression expression) { - if (expression instanceof BinaryExpression) { - Set resultSet = - collectPaths(((BinaryExpression) expression).getLeftExpression()); - resultSet.addAll(collectPaths(((BinaryExpression) expression).getRightExpression())); - return resultSet; - } else if (expression instanceof UnaryExpression) { - return collectPaths(((UnaryExpression) expression).getExpression()); - } else if (expression instanceof FunctionExpression) { - Set resultSet = new HashSet<>(); - for (Expression childExpression : expression.getExpressions()) { - resultSet.addAll(collectPaths(childExpression)); + List resultExpressions = new ArrayList<>(); + for (Expression inputExpression : expression.getExpressions()) { + resultExpressions.addAll(searchAggregationExpressions(inputExpression)); } - return resultSet; - } else if (expression instanceof TimeSeriesOperand) { - return Sets.newHashSet(((TimeSeriesOperand) expression).getPath()); - } else if (expression instanceof TimestampOperand || expression instanceof ConstantOperand) { - return Collections.emptySet(); + return resultExpressions; + } else if (expression instanceof LeafOperand) { + return Collections.emptyList(); } else { throw new IllegalArgumentException( "unsupported expression type: " + expression.getExpressionType()); @@ -632,19 +679,8 @@ public static void updateTypeProvider(Expression expression, TypeProvider typePr } else if (expression instanceof UnaryExpression) { updateTypeProvider(((UnaryExpression) expression).getExpression(), typeProvider); } else if (expression instanceof FunctionExpression) { - if (expression.isBuiltInAggregationFunctionExpression()) { - Validate.isTrue(expression.getExpressions().size() == 1); - Expression childExpression = expression.getExpressions().get(0); - PartialPath path = ((TimeSeriesOperand) childExpression).getPath(); - typeProvider.setType( - expression.getExpressionString(), - SchemaUtils.getSeriesTypeByPath( - path, ((FunctionExpression) expression).getFunctionName())); + for (Expression childExpression : expression.getExpressions()) { updateTypeProvider(childExpression, typeProvider); - } else { - for (Expression childExpression : expression.getExpressions()) { - updateTypeProvider(childExpression, typeProvider); - } } } else if (expression instanceof TimeSeriesOperand) { PartialPath rawPath = ((TimeSeriesOperand) expression).getPath(); @@ -708,60 +744,6 @@ public static Expression removeAliasFromExpression(Expression expression) { } } - /** - * Remove alias from measurement expression according to map. This method is used in ALIGN BY - * DEVICE query. eg: status -> s2, sum(status) -> sum(s2) - * - * @param aliasToMeasurementNameMap a map from alias to measurement name - * @return expression after removing alias - */ - public static Expression removeAliasInMeasurementExpression( - Expression expression, Map aliasToMeasurementNameMap) { - if (expression instanceof BinaryExpression) { - Expression leftExpression = - removeAliasInMeasurementExpression( - ((BinaryExpression) expression).getLeftExpression(), aliasToMeasurementNameMap); - Expression rightExpression = - removeAliasInMeasurementExpression( - ((BinaryExpression) expression).getRightExpression(), aliasToMeasurementNameMap); - return reconstructBinaryExpressions( - expression.getExpressionType(), - Collections.singletonList(leftExpression), - Collections.singletonList(rightExpression)) - .get(0); - } else if (expression instanceof UnaryExpression) { - Expression childExpression = - removeAliasInMeasurementExpression( - ((UnaryExpression) expression).getExpression(), aliasToMeasurementNameMap); - return reconstructUnaryExpressions( - (UnaryExpression) expression, Collections.singletonList(childExpression)) - .get(0); - } else if (expression instanceof FunctionExpression) { - List childExpressions = new ArrayList<>(); - for (Expression suffixExpression : expression.getExpressions()) { - childExpressions.add( - removeAliasInMeasurementExpression(suffixExpression, aliasToMeasurementNameMap)); - } - return reconstructFunctionExpressions( - (FunctionExpression) expression, Collections.singletonList(childExpressions)) - .get(0); - } else if (expression instanceof TimeSeriesOperand) { - String rawMeasurement = ((TimeSeriesOperand) expression).getPath().getFullPath(); - if (aliasToMeasurementNameMap.containsKey(rawMeasurement)) { - PartialPath newPath = - new PartialPath(new String[] {aliasToMeasurementNameMap.get(rawMeasurement)}); - return new TimeSeriesOperand(newPath); - } - return expression; - } else if (expression instanceof ConstantOperand || expression instanceof TimestampOperand) { - // do nothing - return expression; - } else { - throw new IllegalArgumentException( - "unsupported expression type: " + expression.getExpressionType()); - } - } - /** Check for arithmetic expression, logical expression, UDF. Returns true if it exists. */ public static boolean checkIsNeedTransform(Expression expression) { if (expression instanceof BinaryExpression) { @@ -782,89 +764,65 @@ public static boolean checkIsNeedTransform(Expression expression) { // Method can only be used in source expression ///////////////////////////////////////////////////////////////////////////////////////////////// - public static Expression replacePathInSourceExpression( - Expression expression, PartialPath replacedPath) { - if (expression instanceof TimeSeriesOperand) { - return new TimeSeriesOperand(replacedPath); - } else if (expression instanceof FunctionExpression) { - return new FunctionExpression( - ((FunctionExpression) expression).getFunctionName(), - ((FunctionExpression) expression).getFunctionAttributes(), - Collections.singletonList(new TimeSeriesOperand(replacedPath))); - } else { - throw new IllegalArgumentException( - "unsupported expression type: " + expression.getExpressionType()); - } - } - - public static Expression replacePathInSourceExpression( - Expression expression, String replacedPathString) { - PartialPath replacedPath; - try { - replacedPath = new PartialPath(replacedPathString); - } catch (IllegalPathException e) { - throw new SemanticException("illegal path: " + replacedPathString); - } - return replacePathInSourceExpression(expression, replacedPath); - } - - public static PartialPath getPathInSourceExpression(Expression expression) { - if (expression instanceof TimeSeriesOperand) { - return ((TimeSeriesOperand) expression).getPath(); - } else if (expression instanceof FunctionExpression) { - Validate.isTrue(expression.getExpressions().size() == 1); - Validate.isTrue(expression.getExpressions().get(0) instanceof TimeSeriesOperand); - return ((TimeSeriesOperand) expression.getExpressions().get(0)).getPath(); - } else { - throw new IllegalArgumentException( - "unsupported expression type: " + expression.getExpressionType()); - } - } - public static String getDeviceNameInSourceExpression(Expression expression) { - if (expression instanceof TimeSeriesOperand) { + if (expression instanceof BinaryExpression) { + String leftDeviceName = + getDeviceNameInSourceExpression(((BinaryExpression) expression).getLeftExpression()); + if (leftDeviceName != null) { + return leftDeviceName; + } + return getDeviceNameInSourceExpression(((BinaryExpression) expression).getRightExpression()); + } else if (expression instanceof UnaryExpression) { + return getDeviceNameInSourceExpression(((UnaryExpression) expression).getExpression()); + } else if (expression instanceof TimeSeriesOperand) { return ((TimeSeriesOperand) expression).getPath().getDevice(); } else if (expression instanceof FunctionExpression) { return getDeviceNameInSourceExpression(expression.getExpressions().get(0)); + } else if (expression instanceof ConstantOperand || expression instanceof TimestampOperand) { + return null; } else { throw new IllegalArgumentException( "unsupported expression type: " + expression.getExpressionType()); } } - public static Pair getMeasurementWithAliasInSourceExpression( - Expression expression, String alias) { - if (expression instanceof TimeSeriesOperand) { - String measurement = ((TimeSeriesOperand) expression).getPath().getMeasurement(); - if (alias != null && measurement.equals(IoTDBConstant.ONE_LEVEL_PATH_WILDCARD)) { - throw new SemanticException( - String.format( - "ALIGN BY DEVICE: alias '%s' can only be matched with one measurement", alias)); - } - Expression measurementExpression; - try { - measurementExpression = new TimeSeriesOperand(new PartialPath(measurement)); - return new Pair<>(measurementExpression, alias); - } catch (IllegalPathException e) { - throw new SemanticException("ALIGN BY DEVICE: illegal measurement name: " + measurement); - } + public static Expression getMeasurementExpression(Expression expression) { + if (expression instanceof BinaryExpression) { + Expression leftExpression = + getMeasurementExpression(((BinaryExpression) expression).getLeftExpression()); + Expression rightExpression = + getMeasurementExpression(((BinaryExpression) expression).getRightExpression()); + return reconstructBinaryExpressions( + expression.getExpressionType(), + Collections.singletonList(leftExpression), + Collections.singletonList(rightExpression)) + .get(0); + } else if (expression instanceof UnaryExpression) { + Expression childExpression = + getMeasurementExpression(((UnaryExpression) expression).getExpression()); + return reconstructUnaryExpressions( + (UnaryExpression) expression, Collections.singletonList(childExpression)) + .get(0); } else if (expression instanceof FunctionExpression) { - if (expression.getExpressions().size() > 1) { - throw new SemanticException( - "ALIGN BY DEVICE: prefix path in SELECT clause can only be one measurement or one-layer wildcard."); - } - Expression measurementFunctionExpression = - new FunctionExpression( - ((FunctionExpression) expression).getFunctionName(), - ((FunctionExpression) expression).getFunctionAttributes(), - Collections.singletonList( - getMeasurementWithAliasInSourceExpression( - expression.getExpressions().get(0), alias) - .left)); - return new Pair<>(measurementFunctionExpression, alias); + List childExpressions = new ArrayList<>(); + for (Expression suffixExpression : expression.getExpressions()) { + childExpressions.add(getMeasurementExpression(suffixExpression)); + } + return new FunctionExpression( + ((FunctionExpression) expression).getFunctionName(), + ((FunctionExpression) expression).getFunctionAttributes(), + childExpressions); + } else if (expression instanceof TimeSeriesOperand) { + MeasurementPath rawPath = (MeasurementPath) ((TimeSeriesOperand) expression).getPath(); + PartialPath measurement = new PartialPath(rawPath.getMeasurement(), false); + MeasurementPath measurementWithSchema = + new MeasurementPath(measurement, rawPath.getMeasurementSchema()); + return new TimeSeriesOperand(measurementWithSchema); + } else if (expression instanceof TimestampOperand || expression instanceof ConstantOperand) { + return expression; } else { - throw new SemanticException( - "ALIGN BY DEVICE: prefix path in SELECT clause can only be one measurement or one-layer wildcard."); + throw new IllegalArgumentException( + "unsupported expression type: " + expression.getExpressionType()); } } } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/QueryExecution.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/QueryExecution.java index e7371272b88c..eb289306ef70 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/QueryExecution.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/QueryExecution.java @@ -296,7 +296,7 @@ public boolean hasNextResult() { /** return the result column count without the time column */ @Override public int getOutputValueColumnCount() { - return analysis.getRespDatasetHeader().getColumnHeaders().size(); + return analysis.getRespDatasetHeader().getOutputValueColumnCount(); } @Override diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/ConfigExecution.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/ConfigExecution.java index 669ed49b8443..154365b519fe 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/ConfigExecution.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/ConfigExecution.java @@ -161,7 +161,7 @@ public boolean hasNextResult() { @Override public int getOutputValueColumnCount() { - return datasetHeader.getColumnHeaders().size(); + return datasetHeader.getOutputValueColumnCount(); } @Override diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java index 66880a0a64d3..dab981d3ea18 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java @@ -27,6 +27,7 @@ import org.apache.iotdb.db.exception.sql.SemanticException; import org.apache.iotdb.db.mpp.common.filter.BasicFunctionFilter; import org.apache.iotdb.db.mpp.common.filter.QueryFilter; +import org.apache.iotdb.db.mpp.plan.analyze.ExpressionAnalyzer; import org.apache.iotdb.db.mpp.plan.statement.Statement; import org.apache.iotdb.db.mpp.plan.statement.component.FillComponent; import org.apache.iotdb.db.mpp.plan.statement.component.FillPolicy; @@ -563,7 +564,8 @@ private ResultColumn parseResultColumn(IoTDBSqlParser.ResultColumnContext result if (resultColumnContext.AS() != null) { alias = parseAlias(resultColumnContext.alias()); } - return new ResultColumn(expression, alias); + ResultColumn.ColumnType columnType = ExpressionAnalyzer.identifyOutputColumnType(expression); + return new ResultColumn(expression, alias, columnType); } // From Clause diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/StatementGenerator.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/StatementGenerator.java index e07e11acfcd6..6c3749afb023 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/StatementGenerator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/StatementGenerator.java @@ -100,7 +100,7 @@ public static Statement createStatement(TSRawDataQueryReq rawDataQueryReq, ZoneI fromComponent.addPrefixPath(path); } selectComponent.addResultColumn( - new ResultColumn(new TimeSeriesOperand(new PartialPath("", false)))); + new ResultColumn(new TimeSeriesOperand(new PartialPath("")), ResultColumn.ColumnType.RAW)); // set query filter GreaterEqualExpression leftPredicate = @@ -136,7 +136,7 @@ public static Statement createStatement(TSLastDataQueryReq lastDataQueryReq, Zon fromComponent.addPrefixPath(path); } selectComponent.addResultColumn( - new ResultColumn(new TimeSeriesOperand(new PartialPath("", false)))); + new ResultColumn(new TimeSeriesOperand(new PartialPath("")), ResultColumn.ColumnType.RAW)); // set query filter PartialPath timePath = new PartialPath(TIME, false); diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LocalExecutionPlanner.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LocalExecutionPlanner.java index a6d1d16375fb..6fae3156b083 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LocalExecutionPlanner.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LocalExecutionPlanner.java @@ -133,6 +133,7 @@ import org.apache.iotdb.db.mpp.plan.statement.component.FillPolicy; import org.apache.iotdb.db.mpp.plan.statement.component.OrderBy; import org.apache.iotdb.db.mpp.plan.statement.literal.Literal; +import org.apache.iotdb.db.query.expression.Expression; import org.apache.iotdb.db.utils.datastructure.TimeSelector; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.read.filter.basic.Filter; @@ -797,14 +798,17 @@ public Operator visitRowBasedSeriesAggregate( List aggregators = new ArrayList<>(); Map> layout = makeLayout(node); for (AggregationDescriptor descriptor : node.getAggregationDescriptorList()) { - List outputColumnNames = descriptor.getOutputColumnNames(); + List inputColumnNames = + descriptor.getInputExpressions().stream() + .map(Expression::getExpressionString) + .collect(Collectors.toList()); // it may include double parts - List> inputLocationParts = new ArrayList<>(outputColumnNames.size()); - outputColumnNames.forEach(o -> inputLocationParts.add(layout.get(o))); + List> inputLocationParts = new ArrayList<>(inputColumnNames.size()); + inputColumnNames.forEach(o -> inputLocationParts.add(layout.get(o))); List inputLocationList = new ArrayList<>(); for (int i = 0; i < inputLocationParts.get(0).size(); i++) { - if (outputColumnNames.size() == 1) { + if (inputColumnNames.size() == 1) { inputLocationList.add(new InputLocation[] {inputLocationParts.get(0).get(i)}); } else { inputLocationList.add( diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanBuilder.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanBuilder.java index 3b8bbf87eda4..45dab8de2d86 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanBuilder.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanBuilder.java @@ -102,33 +102,28 @@ public LogicalPlanBuilder withNewRoot(PlanNode newRoot) { } public LogicalPlanBuilder planRawDataSource( - Map> deviceNameToSourceExpressions, - OrderBy scanOrder, - Filter timeFilter) { + Set sourceExpressions, OrderBy scanOrder, Filter timeFilter) { List sourceNodeList = new ArrayList<>(); - for (Set sourceExpressionList : - deviceNameToSourceExpressions.values()) { // for each device - List selectedPaths = - sourceExpressionList.stream() - .map(expression -> ((TimeSeriesOperand) expression).getPath()) - .collect(Collectors.toList()); - List groupedPaths = MetaUtils.groupAlignedPaths(selectedPaths); - for (PartialPath path : groupedPaths) { - if (path instanceof MeasurementPath) { // non-aligned series - SeriesScanNode seriesScanNode = - new SeriesScanNode( - context.getQueryId().genPlanNodeId(), (MeasurementPath) path, scanOrder); - seriesScanNode.setTimeFilter(timeFilter); - sourceNodeList.add(seriesScanNode); - } else if (path instanceof AlignedPath) { // aligned series - AlignedSeriesScanNode alignedSeriesScanNode = - new AlignedSeriesScanNode( - context.getQueryId().genPlanNodeId(), (AlignedPath) path, scanOrder); - alignedSeriesScanNode.setTimeFilter(timeFilter); - sourceNodeList.add(alignedSeriesScanNode); - } else { - throw new IllegalArgumentException("unexpected path type"); - } + List selectedPaths = + sourceExpressions.stream() + .map(expression -> ((TimeSeriesOperand) expression).getPath()) + .collect(Collectors.toList()); + List groupedPaths = MetaUtils.groupAlignedPaths(selectedPaths); + for (PartialPath path : groupedPaths) { + if (path instanceof MeasurementPath) { // non-aligned series + SeriesScanNode seriesScanNode = + new SeriesScanNode( + context.getQueryId().genPlanNodeId(), (MeasurementPath) path, scanOrder); + seriesScanNode.setTimeFilter(timeFilter); + sourceNodeList.add(seriesScanNode); + } else if (path instanceof AlignedPath) { // aligned series + AlignedSeriesScanNode alignedSeriesScanNode = + new AlignedSeriesScanNode( + context.getQueryId().genPlanNodeId(), (AlignedPath) path, scanOrder); + alignedSeriesScanNode.setTimeFilter(timeFilter); + sourceNodeList.add(alignedSeriesScanNode); + } else { + throw new IllegalArgumentException("unexpected path type"); } } @@ -137,11 +132,11 @@ public LogicalPlanBuilder planRawDataSource( } public LogicalPlanBuilder planAggregationSource( - Map> deviceNameToSourceExpressions, + Set sourceExpressions, OrderBy scanOrder, Filter timeFilter, GroupByTimeParameter groupByTimeParameter, - Map> aggregationExpressions, + Set aggregationExpressions, Map> groupByLevelExpressions, TypeProvider typeProvider) { AggregationStep curStep = @@ -151,59 +146,56 @@ public LogicalPlanBuilder planAggregationSource( : AggregationStep.SINGLE; List sourceNodeList = new ArrayList<>(); - for (Set sourceExpressionList : - deviceNameToSourceExpressions.values()) { // for each device - Map> ascendingAggregations = new HashMap<>(); - Map> descendingAggregations = new HashMap<>(); - for (Expression sourceExpression : sourceExpressionList) { - AggregationType aggregationFunction = - AggregationType.valueOf( - ((FunctionExpression) sourceExpression).getFunctionName().toUpperCase()); - AggregationDescriptor aggregationDescriptor = - new AggregationDescriptor( - aggregationFunction, curStep, sourceExpression.getExpressions()); - if (curStep.isOutputPartial()) { - updateTypeProviderByPartialAggregation(aggregationDescriptor, typeProvider); - } - PartialPath selectPath = - ((TimeSeriesOperand) sourceExpression.getExpressions().get(0)).getPath(); - if (SchemaUtils.isConsistentWithScanOrder(aggregationFunction, scanOrder)) { - ascendingAggregations - .computeIfAbsent(selectPath, key -> new ArrayList<>()) - .add(aggregationDescriptor); - } else { - descendingAggregations - .computeIfAbsent(selectPath, key -> new ArrayList<>()) - .add(aggregationDescriptor); - } - } - - Map> groupedAscendingAggregations = - MetaUtils.groupAlignedAggregations(ascendingAggregations); - Map> groupedDescendingAggregations = - MetaUtils.groupAlignedAggregations(descendingAggregations); - for (Map.Entry> pathAggregationsEntry : - groupedAscendingAggregations.entrySet()) { - sourceNodeList.add( - createAggregationScanNode( - pathAggregationsEntry.getKey(), - pathAggregationsEntry.getValue(), - scanOrder, - groupByTimeParameter, - timeFilter)); + Map> ascendingAggregations = new HashMap<>(); + Map> descendingAggregations = new HashMap<>(); + for (Expression sourceExpression : sourceExpressions) { + AggregationType aggregationFunction = + AggregationType.valueOf( + ((FunctionExpression) sourceExpression).getFunctionName().toUpperCase()); + AggregationDescriptor aggregationDescriptor = + new AggregationDescriptor( + aggregationFunction, curStep, sourceExpression.getExpressions()); + if (curStep.isOutputPartial()) { + updateTypeProviderByPartialAggregation(aggregationDescriptor, typeProvider); } - for (Map.Entry> pathAggregationsEntry : - groupedDescendingAggregations.entrySet()) { - sourceNodeList.add( - createAggregationScanNode( - pathAggregationsEntry.getKey(), - pathAggregationsEntry.getValue(), - scanOrder, - groupByTimeParameter, - timeFilter)); + PartialPath selectPath = + ((TimeSeriesOperand) sourceExpression.getExpressions().get(0)).getPath(); + if (SchemaUtils.isConsistentWithScanOrder(aggregationFunction, scanOrder)) { + ascendingAggregations + .computeIfAbsent(selectPath, key -> new ArrayList<>()) + .add(aggregationDescriptor); + } else { + descendingAggregations + .computeIfAbsent(selectPath, key -> new ArrayList<>()) + .add(aggregationDescriptor); } } + Map> groupedAscendingAggregations = + MetaUtils.groupAlignedAggregations(ascendingAggregations); + Map> groupedDescendingAggregations = + MetaUtils.groupAlignedAggregations(descendingAggregations); + for (Map.Entry> pathAggregationsEntry : + groupedAscendingAggregations.entrySet()) { + sourceNodeList.add( + createAggregationScanNode( + pathAggregationsEntry.getKey(), + pathAggregationsEntry.getValue(), + scanOrder, + groupByTimeParameter, + timeFilter)); + } + for (Map.Entry> pathAggregationsEntry : + groupedDescendingAggregations.entrySet()) { + sourceNodeList.add( + createAggregationScanNode( + pathAggregationsEntry.getKey(), + pathAggregationsEntry.getValue(), + scanOrder, + groupByTimeParameter, + timeFilter)); + } + if (curStep.isOutputPartial()) { if (groupByTimeParameter != null && groupByTimeParameter.hasOverlap()) { curStep = @@ -287,7 +279,7 @@ public LogicalPlanBuilder planGroupByLevel( } public LogicalPlanBuilder planAggregation( - Map> aggregationExpressions, + Set aggregationExpressions, GroupByTimeParameter groupByTimeParameter, AggregationStep curStep, TypeProvider typeProvider) { @@ -313,7 +305,7 @@ public LogicalPlanBuilder planAggregation( } public LogicalPlanBuilder planGroupByTime( - Map> aggregationExpressions, + Set aggregationExpressions, GroupByTimeParameter groupByTimeParameter, AggregationStep curStep) { if (aggregationExpressions == null) { @@ -331,7 +323,7 @@ public LogicalPlanBuilder planGroupByTime( private PlanNode createGroupByTimeNode( List children, - Map> aggregationExpressions, + Set aggregationExpressions, GroupByTimeParameter groupByTimeParameter, AggregationStep curStep) { List aggregationDescriptorList = @@ -399,9 +391,8 @@ private PlanNode createAggregationScanNode( } private List constructAggregationDescriptorList( - Map> aggregationExpressions, AggregationStep curStep) { - return aggregationExpressions.values().stream() - .flatMap(Set::stream) + Set aggregationExpressions, AggregationStep curStep) { + return aggregationExpressions.stream() .map( expression -> { Validate.isTrue(expression instanceof FunctionExpression); diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanner.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanner.java index a4628d6ef2c0..1265b4d6ab3b 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanner.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanner.java @@ -56,9 +56,6 @@ import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowTimeSeriesStatement; import org.apache.iotdb.db.query.expression.Expression; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; - import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -114,19 +111,17 @@ public PlanNode visitQuery(QueryStatement queryStatement, MPPQueryContext contex if (queryStatement.isAlignByDevice()) { Map deviceToSubPlanMap = new HashMap<>(); - for (String deviceName : analysis.getSourceExpressions().keySet()) { + for (String deviceName : analysis.getDeviceToSourceExpressions().keySet()) { LogicalPlanBuilder subPlanBuilder = new LogicalPlanBuilder(context); subPlanBuilder = subPlanBuilder.withNewRoot( visitQueryBody( queryStatement, - Maps.asMap( - Sets.newHashSet(deviceName), - (key) -> analysis.getSourceExpressions().get(key)), - Maps.asMap( - Sets.newHashSet(deviceName), - (key) -> analysis.getAggregationExpressions().get(key)), - analysis.getSourceExpressions().get(deviceName), + analysis.getDeviceToIsRawDataSource().get(deviceName), + analysis.getDeviceToSourceExpressions().get(deviceName), + analysis.getDeviceToAggregationExpressions().get(deviceName), + analysis.getDeviceToAggregationTransformExpressions().get(deviceName), + analysis.getDeviceToTransformExpressions().get(deviceName), analysis.getDeviceToQueryFilter() != null ? analysis.getDeviceToQueryFilter().get(deviceName) : null, @@ -147,9 +142,11 @@ public PlanNode visitQuery(QueryStatement queryStatement, MPPQueryContext contex planBuilder.withNewRoot( visitQueryBody( queryStatement, + analysis.isRawDataSource(), analysis.getSourceExpressions(), analysis.getAggregationExpressions(), - analysis.getSelectExpressions(), + analysis.getAggregationTransformExpressions(), + analysis.getTransformExpressions(), analysis.getQueryFilter(), context)); } @@ -167,15 +164,14 @@ public PlanNode visitQuery(QueryStatement queryStatement, MPPQueryContext contex public PlanNode visitQueryBody( QueryStatement queryStatement, - Map> sourceExpressions, - Map> aggregationExpressions, - Set selectExpressions, + boolean isRawDataSource, + Set sourceExpressions, + Set aggregationExpressions, + Set aggregationTransformExpressions, + Set transformExpressions, Expression queryFilter, MPPQueryContext context) { LogicalPlanBuilder planBuilder = new LogicalPlanBuilder(context); - boolean isRawDataSource = - !queryStatement.isAggregationQuery() - || (queryStatement.isAggregationQuery() && analysis.hasValueFilter()); // plan data source node if (isRawDataSource) { @@ -188,9 +184,13 @@ public PlanNode visitQueryBody( planBuilder = planBuilder.planFilterAndTransform( queryFilter, - sourceExpressions.values().stream() - .flatMap(Set::stream) - .collect(Collectors.toSet()), + aggregationTransformExpressions, + queryStatement.isGroupByTime(), + queryStatement.getSelectComponent().getZoneId()); + } else { + planBuilder = + planBuilder.planTransform( + aggregationTransformExpressions, queryStatement.isGroupByTime(), queryStatement.getSelectComponent().getZoneId()); } @@ -199,7 +199,8 @@ public PlanNode visitQueryBody( queryStatement.isGroupByLevel() || (queryStatement.isGroupByTime() && analysis.getGroupByTimeParameter().hasOverlap()); - AggregationStep curStep = outputPartial ? AggregationStep.PARTIAL : AggregationStep.FINAL; + AggregationStep curStep = + outputPartial ? AggregationStep.PARTIAL : AggregationStep.SINGLE; planBuilder = planBuilder.planAggregation( aggregationExpressions, @@ -224,18 +225,24 @@ public PlanNode visitQueryBody( planBuilder.planGroupByLevel(analysis.getGroupByLevelExpressions(), curStep); } } + + planBuilder = + planBuilder.planTransform( + transformExpressions, + queryStatement.isGroupByTime(), + queryStatement.getSelectComponent().getZoneId()); } else { if (analysis.hasValueFilter()) { planBuilder = planBuilder.planFilterAndTransform( queryFilter, - selectExpressions, + transformExpressions, queryStatement.isGroupByTime(), queryStatement.getSelectComponent().getZoneId()); } else { planBuilder = planBuilder.planTransform( - selectExpressions, + transformExpressions, queryStatement.isGroupByTime(), queryStatement.getSelectComponent().getZoneId()); } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/ResultColumn.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/ResultColumn.java index 82a8ab4af328..a138f6bfa14c 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/ResultColumn.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/ResultColumn.java @@ -66,13 +66,17 @@ public class ResultColumn extends StatementNode { private final Expression expression; private final String alias; - public ResultColumn(Expression expression, String alias) { + private final ColumnType columnType; + + public ResultColumn(Expression expression, String alias, ColumnType columnType) { this.expression = expression; this.alias = alias; + this.columnType = columnType; } - public ResultColumn(Expression expression) { + public ResultColumn(Expression expression, ColumnType columnType) { this.expression = expression; + this.columnType = columnType; alias = null; } @@ -88,6 +92,10 @@ public String getAlias() { return alias; } + public ColumnType getColumnType() { + return columnType; + } + @Override public String toString() { return "ResultColumn{" + "expression=" + expression + ", alias='" + alias + '\'' + '}'; @@ -109,4 +117,10 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(expression, alias); } + + public enum ColumnType { + RAW, + AGGREGATION, + CONSTANT + } } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/SelectComponent.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/SelectComponent.java index b104041390da..3131d23ad5f1 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/SelectComponent.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/SelectComponent.java @@ -35,8 +35,6 @@ public class SelectComponent extends StatementNode { private boolean hasLast = false; private boolean hasBuiltInAggregationFunction = false; - private boolean hasTimeSeriesGeneratingFunction = false; - private boolean hasUserDefinedAggregationFunction = false; private List resultColumns = new ArrayList<>(); @@ -54,25 +52,12 @@ public boolean isHasBuiltInAggregationFunction() { return hasBuiltInAggregationFunction; } - public boolean isHasTimeSeriesGeneratingFunction() { - return hasTimeSeriesGeneratingFunction; - } - - public boolean isHasUserDefinedAggregationFunction() { - return hasUserDefinedAggregationFunction; - } - public void addResultColumn(ResultColumn resultColumn) { resultColumns.add(resultColumn); - if (resultColumn.getExpression().isUserDefinedAggregationFunctionExpression()) { - hasUserDefinedAggregationFunction = true; - } - if (resultColumn.getExpression().isBuiltInAggregationFunctionExpression()) { + ResultColumn.ColumnType columnType = resultColumn.getColumnType(); + if (columnType == ResultColumn.ColumnType.AGGREGATION) { hasBuiltInAggregationFunction = true; } - if (resultColumn.getExpression().isTimeSeriesGeneratingFunctionExpression()) { - hasTimeSeriesGeneratingFunction = true; - } } public void setResultColumns(List resultColumns) { @@ -98,4 +83,8 @@ public boolean isHasLast() { public void setHasLast(boolean hasLast) { this.hasLast = hasLast; } + + public void setHasBuiltInAggregationFunction(boolean hasBuiltInAggregationFunction) { + this.hasBuiltInAggregationFunction = hasBuiltInAggregationFunction; + } } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/crud/QueryStatement.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/crud/QueryStatement.java index 75dbc40439ae..a626c17e0c80 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/crud/QueryStatement.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/crud/QueryStatement.java @@ -227,19 +227,21 @@ public boolean disableAlign() { return resultSetFormat == ResultSetFormat.DISABLE_ALIGN; } - public boolean HasBuiltInAggregationFunction() { - return selectComponent.isHasBuiltInAggregationFunction(); - } - - public boolean hasTimeSeriesGeneratingFunction() { - return selectComponent.isHasTimeSeriesGeneratingFunction(); - } - - public boolean hasUserDefinedAggregationFunction() { - return selectComponent.isHasUserDefinedAggregationFunction(); - } - public void semanticCheck() { + if (isAggregationQuery()) { + if (disableAlign()) { + throw new SemanticException("AGGREGATION doesn't support disable align clause."); + } + if (isGroupByLevel() && isAlignByDevice()) { + throw new SemanticException("group by level does not support align by device now."); + } + for (ResultColumn resultColumn : selectComponent.getResultColumns()) { + if (resultColumn.getColumnType() != ResultColumn.ColumnType.AGGREGATION) { + throw new SemanticException("Raw data and aggregation hybrid query is not supported."); + } + } + } + if (isAlignByDevice()) { // the paths can only be measurement or one-level wildcard in ALIGN BY DEVICE for (ResultColumn resultColumn : selectComponent.getResultColumns()) { @@ -264,22 +266,6 @@ public void semanticCheck() { } } } - - if (isAggregationQuery()) { - if (disableAlign()) { - throw new SemanticException("AGGREGATION doesn't support disable align clause."); - } - if (isGroupByLevel() && isAlignByDevice()) { - throw new SemanticException("group by level does not support align by device now."); - } - if (hasTimeSeriesGeneratingFunction()) { - throw new SemanticException( - "User-defined and built-in hybrid aggregation is not supported together."); - } - for (ResultColumn resultColumn : selectComponent.getResultColumns()) { - ExpressionAnalyzer.checkIsAllAggregation(resultColumn.getExpression()); - } - } } @Override diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/plan/analyze/AnalyzeFailTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/plan/analyze/AnalyzeFailTest.java index 6211ca4c782c..b46ab02c0783 100644 --- a/server/src/test/java/org/apache/iotdb/db/mpp/plan/analyze/AnalyzeFailTest.java +++ b/server/src/test/java/org/apache/iotdb/db/mpp/plan/analyze/AnalyzeFailTest.java @@ -33,6 +33,24 @@ public class AnalyzeFailTest { + @Test + public void illegalAggregationTest1() { + String message = "Raw data and aggregation result hybrid calculation is not supported."; + assertAnalyzeSemanticException("SELECT sum(s1) + s1 FROM root.sg.d1", message); + } + + @Test + public void illegalAggregationTest2() { + String message = "Aggregation results cannot be as input of the aggregation function."; + assertAnalyzeSemanticException("SELECT sum(sum(s1)) FROM root.sg.d1", message); + } + + @Test + public void illegalAggregationTest3() { + String message = "Raw data and aggregation hybrid query is not supported."; + assertAnalyzeSemanticException("SELECT sum(s1), s1 FROM root.sg.d1", message); + } + @Test public void samePropertyKeyTest() { assertAnalyzeSemanticException( diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/LogicalPlannerTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/LogicalPlannerTest.java index 7b5b3c5f3331..06919ca3bfa4 100644 --- a/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/LogicalPlannerTest.java +++ b/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/LogicalPlannerTest.java @@ -71,6 +71,7 @@ public class LogicalPlannerTest { public void testQueryPlan() { for (String sql : querySQLs) { Assert.assertEquals(sqlToPlanMap.get(sql), parseSQLToPlanNode(sql)); + System.out.printf("\"%s\" TEST PASSED\n", sql); } } diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/QueryLogicalPlanUtil.java b/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/QueryLogicalPlanUtil.java index 6248ed77911b..411396ecd30c 100644 --- a/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/QueryLogicalPlanUtil.java +++ b/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/QueryLogicalPlanUtil.java @@ -23,9 +23,9 @@ import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.metadata.path.AlignedPath; import org.apache.iotdb.db.metadata.path.MeasurementPath; +import org.apache.iotdb.db.mpp.common.QueryId; import org.apache.iotdb.db.mpp.common.header.HeaderConstant; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNode; -import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId; import org.apache.iotdb.db.mpp.plan.planner.plan.node.process.AggregationNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.process.DeviceViewNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.process.FilterNode; @@ -105,31 +105,32 @@ public class QueryLogicalPlanUtil { static { String sql = "SELECT ** FROM root.sg.d2 LIMIT 10 OFFSET 10"; + QueryId queryId = new QueryId("test"); List sourceNodeList = new ArrayList<>(); + sourceNodeList.add( + new AlignedSeriesScanNode( + queryId.genPlanNodeId(), + (AlignedPath) schemaMap.get("root.sg.d2.a"), + OrderBy.TIMESTAMP_ASC)); sourceNodeList.add( new SeriesScanNode( - new PlanNodeId("0"), + queryId.genPlanNodeId(), (MeasurementPath) schemaMap.get("root.sg.d2.s1"), OrderBy.TIMESTAMP_ASC)); sourceNodeList.add( new SeriesScanNode( - new PlanNodeId("1"), + queryId.genPlanNodeId(), (MeasurementPath) schemaMap.get("root.sg.d2.s2"), OrderBy.TIMESTAMP_ASC)); sourceNodeList.add( new SeriesScanNode( - new PlanNodeId("2"), + queryId.genPlanNodeId(), (MeasurementPath) schemaMap.get("root.sg.d2.s4"), OrderBy.TIMESTAMP_ASC)); - sourceNodeList.add( - new AlignedSeriesScanNode( - new PlanNodeId("3"), - (AlignedPath) schemaMap.get("root.sg.d2.a"), - OrderBy.TIMESTAMP_ASC)); TimeJoinNode timeJoinNode = - new TimeJoinNode(new PlanNodeId("4"), OrderBy.TIMESTAMP_ASC, sourceNodeList); - OffsetNode offsetNode = new OffsetNode(new PlanNodeId("5"), timeJoinNode, 10); - LimitNode limitNode = new LimitNode(new PlanNodeId("6"), offsetNode, 10); + new TimeJoinNode(queryId.genPlanNodeId(), OrderBy.TIMESTAMP_ASC, sourceNodeList); + OffsetNode offsetNode = new OffsetNode(queryId.genPlanNodeId(), timeJoinNode, 10); + LimitNode limitNode = new LimitNode(queryId.genPlanNodeId(), offsetNode, 10); querySQLs.add(sql); sqlToPlanMap.put(sql, limitNode); @@ -141,27 +142,28 @@ public class QueryLogicalPlanUtil { "SELECT s1 FROM root.sg.* WHERE time > 100 and s2 > 10 " + "ORDER BY TIME DESC WITHOUT NULL ANY LIMIT 100 OFFSET 100 SLIMIT 1 SOFFSET 1"; + QueryId queryId = new QueryId("test"); List sourceNodeList = new ArrayList<>(); sourceNodeList.add( new SeriesScanNode( - new PlanNodeId("0"), - (MeasurementPath) schemaMap.get("root.sg.d1.s2"), + queryId.genPlanNodeId(), + (MeasurementPath) schemaMap.get("root.sg.d2.s1"), OrderBy.TIMESTAMP_DESC)); sourceNodeList.add( new SeriesScanNode( - new PlanNodeId("1"), - (MeasurementPath) schemaMap.get("root.sg.d2.s1"), + queryId.genPlanNodeId(), + (MeasurementPath) schemaMap.get("root.sg.d2.s2"), OrderBy.TIMESTAMP_DESC)); sourceNodeList.add( new SeriesScanNode( - new PlanNodeId("2"), - (MeasurementPath) schemaMap.get("root.sg.d2.s2"), + queryId.genPlanNodeId(), + (MeasurementPath) schemaMap.get("root.sg.d1.s2"), OrderBy.TIMESTAMP_DESC)); sourceNodeList.forEach( planNode -> ((SeriesScanNode) planNode).setTimeFilter(TimeFilter.gt(100))); TimeJoinNode timeJoinNode = - new TimeJoinNode(new PlanNodeId("3"), OrderBy.TIMESTAMP_DESC, sourceNodeList); + new TimeJoinNode(queryId.genPlanNodeId(), OrderBy.TIMESTAMP_DESC, sourceNodeList); GreaterThanExpression timeFilter = new GreaterThanExpression( @@ -179,7 +181,7 @@ public class QueryLogicalPlanUtil { FilterNode filterNode = new FilterNode( - new PlanNodeId("4"), + queryId.genPlanNodeId(), timeJoinNode, new Expression[] {new TimeSeriesOperand(schemaMap.get("root.sg.d2.s1"))}, predicate, @@ -188,13 +190,13 @@ public class QueryLogicalPlanUtil { FilterNullNode filterNullNode = new FilterNullNode( - new PlanNodeId("5"), + queryId.genPlanNodeId(), filterNode, FilterNullPolicy.CONTAINS_NULL, Collections.singletonList(new TimeSeriesOperand(schemaMap.get("root.sg.d2.s1")))); - OffsetNode offsetNode = new OffsetNode(new PlanNodeId("6"), filterNullNode, 100); - LimitNode limitNode = new LimitNode(new PlanNodeId("7"), offsetNode, 100); + OffsetNode offsetNode = new OffsetNode(queryId.genPlanNodeId(), filterNullNode, 100); + LimitNode limitNode = new LimitNode(queryId.genPlanNodeId(), offsetNode, 100); querySQLs.add(sql); sqlToPlanMap.put(sql, limitNode); @@ -206,27 +208,28 @@ public class QueryLogicalPlanUtil { "SELECT * FROM root.sg.* WHERE time > 100 and s1 > 10 " + "ORDER BY TIME DESC LIMIT 100 OFFSET 100 ALIGN BY DEVICE"; + QueryId queryId = new QueryId("test"); List sourceNodeList1 = new ArrayList<>(); sourceNodeList1.add( new SeriesScanNode( - new PlanNodeId("0"), + queryId.genPlanNodeId(), (MeasurementPath) schemaMap.get("root.sg.d1.s3"), OrderBy.TIMESTAMP_DESC)); sourceNodeList1.add( new SeriesScanNode( - new PlanNodeId("1"), + queryId.genPlanNodeId(), (MeasurementPath) schemaMap.get("root.sg.d1.s1"), OrderBy.TIMESTAMP_DESC)); sourceNodeList1.add( new SeriesScanNode( - new PlanNodeId("2"), + queryId.genPlanNodeId(), (MeasurementPath) schemaMap.get("root.sg.d1.s2"), OrderBy.TIMESTAMP_DESC)); sourceNodeList1.forEach( planNode -> ((SeriesScanNode) planNode).setTimeFilter(TimeFilter.gt(100))); TimeJoinNode timeJoinNode1 = - new TimeJoinNode(new PlanNodeId("3"), OrderBy.TIMESTAMP_DESC, sourceNodeList1); + new TimeJoinNode(queryId.genPlanNodeId(), OrderBy.TIMESTAMP_DESC, sourceNodeList1); GreaterThanExpression timeFilter = new GreaterThanExpression( @@ -239,7 +242,7 @@ public class QueryLogicalPlanUtil { FilterNode filterNode1 = new FilterNode( - new PlanNodeId("4"), + queryId.genPlanNodeId(), timeJoinNode1, new Expression[] { new TimeSeriesOperand(schemaMap.get("root.sg.d1.s3")), @@ -253,24 +256,24 @@ public class QueryLogicalPlanUtil { List sourceNodeList2 = new ArrayList<>(); sourceNodeList2.add( new SeriesScanNode( - new PlanNodeId("5"), - (MeasurementPath) schemaMap.get("root.sg.d2.s4"), + queryId.genPlanNodeId(), + (MeasurementPath) schemaMap.get("root.sg.d2.s1"), OrderBy.TIMESTAMP_DESC)); sourceNodeList2.add( new SeriesScanNode( - new PlanNodeId("6"), - (MeasurementPath) schemaMap.get("root.sg.d2.s1"), + queryId.genPlanNodeId(), + (MeasurementPath) schemaMap.get("root.sg.d2.s2"), OrderBy.TIMESTAMP_DESC)); sourceNodeList2.add( new SeriesScanNode( - new PlanNodeId("7"), - (MeasurementPath) schemaMap.get("root.sg.d2.s2"), + queryId.genPlanNodeId(), + (MeasurementPath) schemaMap.get("root.sg.d2.s4"), OrderBy.TIMESTAMP_DESC)); sourceNodeList2.forEach( planNode -> ((SeriesScanNode) planNode).setTimeFilter(TimeFilter.gt(100))); TimeJoinNode timeJoinNode2 = - new TimeJoinNode(new PlanNodeId("8"), OrderBy.TIMESTAMP_DESC, sourceNodeList2); + new TimeJoinNode(queryId.genPlanNodeId(), OrderBy.TIMESTAMP_DESC, sourceNodeList2); GreaterThanExpression valueFilter2 = new GreaterThanExpression( @@ -280,31 +283,31 @@ public class QueryLogicalPlanUtil { FilterNode filterNode2 = new FilterNode( - new PlanNodeId("9"), + queryId.genPlanNodeId(), timeJoinNode2, new Expression[] { - new TimeSeriesOperand(schemaMap.get("root.sg.d2.s4")), new TimeSeriesOperand(schemaMap.get("root.sg.d2.s1")), - new TimeSeriesOperand(schemaMap.get("root.sg.d2.s2")) + new TimeSeriesOperand(schemaMap.get("root.sg.d2.s2")), + new TimeSeriesOperand(schemaMap.get("root.sg.d2.s4")) }, predicate2, false, ZonedDateTime.now().getOffset()); Map> deviceToMeasurementIndexesMap = new HashMap<>(); - deviceToMeasurementIndexesMap.put("root.sg.d1", Arrays.asList(1, 3, 4)); + deviceToMeasurementIndexesMap.put("root.sg.d1", Arrays.asList(1, 2, 3)); deviceToMeasurementIndexesMap.put("root.sg.d2", Arrays.asList(2, 3, 4)); DeviceViewNode deviceViewNode = new DeviceViewNode( - new PlanNodeId("10"), + queryId.genPlanNodeId(), Arrays.asList(OrderBy.DEVICE_ASC, OrderBy.TIMESTAMP_DESC), - Arrays.asList(HeaderConstant.COLUMN_DEVICE, "s3", "s4", "s1", "s2"), + Arrays.asList(HeaderConstant.COLUMN_DEVICE, "s3", "s1", "s2", "s4"), deviceToMeasurementIndexesMap); deviceViewNode.addChildDeviceNode("root.sg.d1", filterNode1); deviceViewNode.addChildDeviceNode("root.sg.d2", filterNode2); - OffsetNode offsetNode = new OffsetNode(new PlanNodeId("11"), deviceViewNode, 100); - LimitNode limitNode = new LimitNode(new PlanNodeId("12"), offsetNode, 100); + OffsetNode offsetNode = new OffsetNode(queryId.genPlanNodeId(), deviceViewNode, 100); + LimitNode limitNode = new LimitNode(queryId.genPlanNodeId(), offsetNode, 100); querySQLs.add(sql); sqlToPlanMap.put(sql, limitNode); @@ -315,59 +318,53 @@ public class QueryLogicalPlanUtil { String sql = "SELECT last_value(s1), first_value(s1), sum(s2) FROM root.sg.** WHERE time > 100 LIMIT 10 OFFSET 10"; + QueryId queryId = new QueryId("test"); List sourceNodeList = new ArrayList<>(); Filter timeFilter = TimeFilter.gt(100); sourceNodeList.add( - new SeriesAggregationScanNode( - new PlanNodeId("0"), - (MeasurementPath) schemaMap.get("root.sg.d1.s1"), - Collections.singletonList( + new AlignedSeriesAggregationScanNode( + queryId.genPlanNodeId(), + (AlignedPath) schemaMap.get("root.sg.d2.a"), + Arrays.asList( new AggregationDescriptor( AggregationType.FIRST_VALUE, AggregationStep.SINGLE, Collections.singletonList( - new TimeSeriesOperand(schemaMap.get("root.sg.d1.s1"))))), - OrderBy.TIMESTAMP_ASC, - null)); - sourceNodeList.add( - new SeriesAggregationScanNode( - new PlanNodeId("1"), - (MeasurementPath) schemaMap.get("root.sg.d1.s2"), - Collections.singletonList( + new TimeSeriesOperand(schemaMap.get("root.sg.d2.a.s1")))), new AggregationDescriptor( AggregationType.SUM, AggregationStep.SINGLE, Collections.singletonList( - new TimeSeriesOperand(schemaMap.get("root.sg.d1.s2"))))), + new TimeSeriesOperand(schemaMap.get("root.sg.d2.a.s2"))))), OrderBy.TIMESTAMP_ASC, null)); sourceNodeList.add( new SeriesAggregationScanNode( - new PlanNodeId("2"), - (MeasurementPath) schemaMap.get("root.sg.d1.s1"), + queryId.genPlanNodeId(), + (MeasurementPath) schemaMap.get("root.sg.d2.s1"), Collections.singletonList( new AggregationDescriptor( - AggregationType.LAST_VALUE, + AggregationType.FIRST_VALUE, AggregationStep.SINGLE, Collections.singletonList( - new TimeSeriesOperand(schemaMap.get("root.sg.d1.s1"))))), + new TimeSeriesOperand(schemaMap.get("root.sg.d2.s1"))))), OrderBy.TIMESTAMP_ASC, null)); sourceNodeList.add( new SeriesAggregationScanNode( - new PlanNodeId("3"), - (MeasurementPath) schemaMap.get("root.sg.d2.s1"), + queryId.genPlanNodeId(), + (MeasurementPath) schemaMap.get("root.sg.d1.s1"), Collections.singletonList( new AggregationDescriptor( AggregationType.FIRST_VALUE, AggregationStep.SINGLE, Collections.singletonList( - new TimeSeriesOperand(schemaMap.get("root.sg.d2.s1"))))), + new TimeSeriesOperand(schemaMap.get("root.sg.d1.s1"))))), OrderBy.TIMESTAMP_ASC, null)); sourceNodeList.add( new SeriesAggregationScanNode( - new PlanNodeId("4"), + queryId.genPlanNodeId(), (MeasurementPath) schemaMap.get("root.sg.d2.s2"), Collections.singletonList( new AggregationDescriptor( @@ -379,43 +376,50 @@ public class QueryLogicalPlanUtil { null)); sourceNodeList.add( new SeriesAggregationScanNode( - new PlanNodeId("5"), - (MeasurementPath) schemaMap.get("root.sg.d2.s1"), + queryId.genPlanNodeId(), + (MeasurementPath) schemaMap.get("root.sg.d1.s2"), Collections.singletonList( new AggregationDescriptor( - AggregationType.LAST_VALUE, + AggregationType.SUM, AggregationStep.SINGLE, Collections.singletonList( - new TimeSeriesOperand(schemaMap.get("root.sg.d2.s1"))))), + new TimeSeriesOperand(schemaMap.get("root.sg.d1.s2"))))), OrderBy.TIMESTAMP_ASC, null)); sourceNodeList.add( new AlignedSeriesAggregationScanNode( - new PlanNodeId("6"), - (AlignedPath) schemaMap.get("root.sg.d2.a"), - Arrays.asList( + queryId.genPlanNodeId(), + new AlignedPath((MeasurementPath) schemaMap.get("root.sg.d2.a.s1")), + Collections.singletonList( new AggregationDescriptor( - AggregationType.FIRST_VALUE, + AggregationType.LAST_VALUE, AggregationStep.SINGLE, Collections.singletonList( - new TimeSeriesOperand(schemaMap.get("root.sg.d2.a.s1")))), + new TimeSeriesOperand(schemaMap.get("root.sg.d2.a.s1"))))), + OrderBy.TIMESTAMP_ASC, + null)); + sourceNodeList.add( + new SeriesAggregationScanNode( + queryId.genPlanNodeId(), + (MeasurementPath) schemaMap.get("root.sg.d2.s1"), + Collections.singletonList( new AggregationDescriptor( - AggregationType.SUM, + AggregationType.LAST_VALUE, AggregationStep.SINGLE, Collections.singletonList( - new TimeSeriesOperand(schemaMap.get("root.sg.d2.a.s2"))))), + new TimeSeriesOperand(schemaMap.get("root.sg.d2.s1"))))), OrderBy.TIMESTAMP_ASC, null)); sourceNodeList.add( - new AlignedSeriesAggregationScanNode( - new PlanNodeId("7"), - new AlignedPath((MeasurementPath) schemaMap.get("root.sg.d2.a.s1")), + new SeriesAggregationScanNode( + queryId.genPlanNodeId(), + (MeasurementPath) schemaMap.get("root.sg.d1.s1"), Collections.singletonList( new AggregationDescriptor( AggregationType.LAST_VALUE, AggregationStep.SINGLE, Collections.singletonList( - new TimeSeriesOperand(schemaMap.get("root.sg.d2.a.s1"))))), + new TimeSeriesOperand(schemaMap.get("root.sg.d1.s1"))))), OrderBy.TIMESTAMP_ASC, null)); sourceNodeList.forEach( @@ -428,9 +432,9 @@ public class QueryLogicalPlanUtil { }); TimeJoinNode timeJoinNode = - new TimeJoinNode(new PlanNodeId("8"), OrderBy.TIMESTAMP_ASC, sourceNodeList); - OffsetNode offsetNode = new OffsetNode(new PlanNodeId("9"), timeJoinNode, 10); - LimitNode limitNode = new LimitNode(new PlanNodeId("10"), offsetNode, 10); + new TimeJoinNode(queryId.genPlanNodeId(), OrderBy.TIMESTAMP_ASC, sourceNodeList); + OffsetNode offsetNode = new OffsetNode(queryId.genPlanNodeId(), timeJoinNode, 10); + LimitNode limitNode = new LimitNode(queryId.genPlanNodeId(), offsetNode, 10); querySQLs.add(sql); sqlToPlanMap.put(sql, limitNode); @@ -442,57 +446,68 @@ public class QueryLogicalPlanUtil { "SELECT count(s1), max_value(s2), last_value(s1) FROM root.sg.** WHERE time > 100 " + "GROUP BY LEVEL = 1 ORDER BY TIME DESC LIMIT 100 OFFSET 100"; + QueryId queryId = new QueryId("test"); List sourceNodeList = new ArrayList<>(); Filter timeFilter = TimeFilter.gt(100); sourceNodeList.add( - new SeriesAggregationScanNode( - new PlanNodeId("0"), - (MeasurementPath) schemaMap.get("root.sg.d1.s1"), + new AlignedSeriesAggregationScanNode( + queryId.genPlanNodeId(), + (AlignedPath) schemaMap.get("root.sg.d2.a"), Arrays.asList( + new AggregationDescriptor( + AggregationType.LAST_VALUE, + AggregationStep.PARTIAL, + Collections.singletonList( + new TimeSeriesOperand(schemaMap.get("root.sg.d2.a.s1")))), new AggregationDescriptor( AggregationType.COUNT, AggregationStep.PARTIAL, Collections.singletonList( - new TimeSeriesOperand(schemaMap.get("root.sg.d1.s1")))), + new TimeSeriesOperand(schemaMap.get("root.sg.d2.a.s1")))), new AggregationDescriptor( - AggregationType.LAST_VALUE, + AggregationType.MAX_VALUE, AggregationStep.PARTIAL, Collections.singletonList( - new TimeSeriesOperand(schemaMap.get("root.sg.d1.s1"))))), + new TimeSeriesOperand(schemaMap.get("root.sg.d2.a.s2"))))), OrderBy.TIMESTAMP_DESC, null)); sourceNodeList.add( new SeriesAggregationScanNode( - new PlanNodeId("1"), - (MeasurementPath) schemaMap.get("root.sg.d1.s2"), - Collections.singletonList( + queryId.genPlanNodeId(), + (MeasurementPath) schemaMap.get("root.sg.d2.s1"), + Arrays.asList( new AggregationDescriptor( - AggregationType.MAX_VALUE, + AggregationType.COUNT, AggregationStep.PARTIAL, Collections.singletonList( - new TimeSeriesOperand(schemaMap.get("root.sg.d1.s2"))))), + new TimeSeriesOperand(schemaMap.get("root.sg.d2.s1")))), + new AggregationDescriptor( + AggregationType.LAST_VALUE, + AggregationStep.PARTIAL, + Collections.singletonList( + new TimeSeriesOperand(schemaMap.get("root.sg.d2.s1"))))), OrderBy.TIMESTAMP_DESC, null)); sourceNodeList.add( new SeriesAggregationScanNode( - new PlanNodeId("2"), - (MeasurementPath) schemaMap.get("root.sg.d2.s1"), + queryId.genPlanNodeId(), + (MeasurementPath) schemaMap.get("root.sg.d1.s1"), Arrays.asList( new AggregationDescriptor( AggregationType.COUNT, AggregationStep.PARTIAL, Collections.singletonList( - new TimeSeriesOperand(schemaMap.get("root.sg.d2.s1")))), + new TimeSeriesOperand(schemaMap.get("root.sg.d1.s1")))), new AggregationDescriptor( AggregationType.LAST_VALUE, AggregationStep.PARTIAL, Collections.singletonList( - new TimeSeriesOperand(schemaMap.get("root.sg.d2.s1"))))), + new TimeSeriesOperand(schemaMap.get("root.sg.d1.s1"))))), OrderBy.TIMESTAMP_DESC, null)); sourceNodeList.add( new SeriesAggregationScanNode( - new PlanNodeId("3"), + queryId.genPlanNodeId(), (MeasurementPath) schemaMap.get("root.sg.d2.s2"), Collections.singletonList( new AggregationDescriptor( @@ -503,25 +518,15 @@ public class QueryLogicalPlanUtil { OrderBy.TIMESTAMP_DESC, null)); sourceNodeList.add( - new AlignedSeriesAggregationScanNode( - new PlanNodeId("4"), - (AlignedPath) schemaMap.get("root.sg.d2.a"), - Arrays.asList( - new AggregationDescriptor( - AggregationType.LAST_VALUE, - AggregationStep.PARTIAL, - Collections.singletonList( - new TimeSeriesOperand(schemaMap.get("root.sg.d2.a.s1")))), - new AggregationDescriptor( - AggregationType.COUNT, - AggregationStep.PARTIAL, - Collections.singletonList( - new TimeSeriesOperand(schemaMap.get("root.sg.d2.a.s1")))), + new SeriesAggregationScanNode( + queryId.genPlanNodeId(), + (MeasurementPath) schemaMap.get("root.sg.d1.s2"), + Collections.singletonList( new AggregationDescriptor( AggregationType.MAX_VALUE, AggregationStep.PARTIAL, Collections.singletonList( - new TimeSeriesOperand(schemaMap.get("root.sg.d2.a.s2"))))), + new TimeSeriesOperand(schemaMap.get("root.sg.d1.s2"))))), OrderBy.TIMESTAMP_DESC, null)); sourceNodeList.forEach( @@ -535,7 +540,7 @@ public class QueryLogicalPlanUtil { GroupByLevelNode groupByLevelNode = new GroupByLevelNode( - new PlanNodeId("5"), + queryId.genPlanNodeId(), sourceNodeList, Arrays.asList( new AggregationDescriptor( @@ -615,8 +620,8 @@ public class QueryLogicalPlanUtil { "last_value(root.sg.*.s1)", "last_value(root.sg.*.*.s1)")); - OffsetNode offsetNode = new OffsetNode(new PlanNodeId("6"), groupByLevelNode, 100); - LimitNode limitNode = new LimitNode(new PlanNodeId("7"), offsetNode, 100); + OffsetNode offsetNode = new OffsetNode(queryId.genPlanNodeId(), groupByLevelNode, 100); + LimitNode limitNode = new LimitNode(queryId.genPlanNodeId(), offsetNode, 100); querySQLs.add(sql); sqlToPlanMap.put(sql, limitNode); @@ -628,11 +633,12 @@ public class QueryLogicalPlanUtil { "SELECT count(s1), max_value(s2), last_value(s1) FROM root.sg.* WHERE time > 100 " + "ORDER BY TIME DESC LIMIT 100 OFFSET 100 ALIGN BY DEVICE"; + QueryId queryId = new QueryId("test"); Filter timeFilter = TimeFilter.gt(100); List sourceNodeList1 = new ArrayList<>(); sourceNodeList1.add( new SeriesAggregationScanNode( - new PlanNodeId("0"), + queryId.genPlanNodeId(), (MeasurementPath) schemaMap.get("root.sg.d1.s1"), Arrays.asList( new AggregationDescriptor( @@ -649,7 +655,7 @@ public class QueryLogicalPlanUtil { null)); sourceNodeList1.add( new SeriesAggregationScanNode( - new PlanNodeId("1"), + queryId.genPlanNodeId(), (MeasurementPath) schemaMap.get("root.sg.d1.s2"), Collections.singletonList( new AggregationDescriptor( @@ -662,12 +668,12 @@ public class QueryLogicalPlanUtil { sourceNodeList1.forEach(node -> ((SeriesAggregationScanNode) node).setTimeFilter(timeFilter)); TimeJoinNode timeJoinNode1 = - new TimeJoinNode(new PlanNodeId("2"), OrderBy.TIMESTAMP_DESC, sourceNodeList1); + new TimeJoinNode(queryId.genPlanNodeId(), OrderBy.TIMESTAMP_DESC, sourceNodeList1); List sourceNodeList2 = new ArrayList<>(); sourceNodeList2.add( new SeriesAggregationScanNode( - new PlanNodeId("3"), + queryId.genPlanNodeId(), (MeasurementPath) schemaMap.get("root.sg.d2.s1"), Arrays.asList( new AggregationDescriptor( @@ -684,7 +690,7 @@ public class QueryLogicalPlanUtil { null)); sourceNodeList2.add( new SeriesAggregationScanNode( - new PlanNodeId("4"), + queryId.genPlanNodeId(), (MeasurementPath) schemaMap.get("root.sg.d2.s2"), Collections.singletonList( new AggregationDescriptor( @@ -697,14 +703,14 @@ public class QueryLogicalPlanUtil { sourceNodeList2.forEach(node -> ((SeriesAggregationScanNode) node).setTimeFilter(timeFilter)); TimeJoinNode timeJoinNode2 = - new TimeJoinNode(new PlanNodeId("5"), OrderBy.TIMESTAMP_DESC, sourceNodeList2); + new TimeJoinNode(queryId.genPlanNodeId(), OrderBy.TIMESTAMP_DESC, sourceNodeList2); Map> deviceToMeasurementIndexesMap = new HashMap<>(); deviceToMeasurementIndexesMap.put("root.sg.d1", Arrays.asList(1, 2, 3)); deviceToMeasurementIndexesMap.put("root.sg.d2", Arrays.asList(1, 2, 3)); DeviceViewNode deviceViewNode = new DeviceViewNode( - new PlanNodeId("6"), + queryId.genPlanNodeId(), Arrays.asList(OrderBy.DEVICE_ASC, OrderBy.TIMESTAMP_DESC), Arrays.asList( HeaderConstant.COLUMN_DEVICE, "count(s1)", "max_value(s2)", "last_value(s1)"), @@ -712,8 +718,8 @@ public class QueryLogicalPlanUtil { deviceViewNode.addChildDeviceNode("root.sg.d1", timeJoinNode1); deviceViewNode.addChildDeviceNode("root.sg.d2", timeJoinNode2); - OffsetNode offsetNode = new OffsetNode(new PlanNodeId("7"), deviceViewNode, 100); - LimitNode limitNode = new LimitNode(new PlanNodeId("8"), offsetNode, 100); + OffsetNode offsetNode = new OffsetNode(queryId.genPlanNodeId(), deviceViewNode, 100); + LimitNode limitNode = new LimitNode(queryId.genPlanNodeId(), offsetNode, 100); querySQLs.add(sql); sqlToPlanMap.put(sql, limitNode); @@ -725,32 +731,33 @@ public class QueryLogicalPlanUtil { "SELECT count(s1), max_value(s2), last_value(s1) FROM root.sg.* WHERE time > 100 and s2 > 10 " + "GROUP BY LEVEL = 1 ORDER BY TIME DESC LIMIT 100 OFFSET 100"; + QueryId queryId = new QueryId("test"); List sourceNodeList = new ArrayList<>(); sourceNodeList.add( new SeriesScanNode( - new PlanNodeId("0"), - (MeasurementPath) schemaMap.get("root.sg.d1.s1"), + queryId.genPlanNodeId(), + (MeasurementPath) schemaMap.get("root.sg.d2.s1"), OrderBy.TIMESTAMP_DESC)); sourceNodeList.add( new SeriesScanNode( - new PlanNodeId("1"), - (MeasurementPath) schemaMap.get("root.sg.d1.s2"), + queryId.genPlanNodeId(), + (MeasurementPath) schemaMap.get("root.sg.d1.s1"), OrderBy.TIMESTAMP_DESC)); sourceNodeList.add( new SeriesScanNode( - new PlanNodeId("2"), - (MeasurementPath) schemaMap.get("root.sg.d2.s1"), + queryId.genPlanNodeId(), + (MeasurementPath) schemaMap.get("root.sg.d2.s2"), OrderBy.TIMESTAMP_DESC)); sourceNodeList.add( new SeriesScanNode( - new PlanNodeId("3"), - (MeasurementPath) schemaMap.get("root.sg.d2.s2"), + queryId.genPlanNodeId(), + (MeasurementPath) schemaMap.get("root.sg.d1.s2"), OrderBy.TIMESTAMP_DESC)); sourceNodeList.forEach( planNode -> ((SeriesScanNode) planNode).setTimeFilter(TimeFilter.gt(100))); TimeJoinNode timeJoinNode = - new TimeJoinNode(new PlanNodeId("4"), OrderBy.TIMESTAMP_DESC, sourceNodeList); + new TimeJoinNode(queryId.genPlanNodeId(), OrderBy.TIMESTAMP_DESC, sourceNodeList); GreaterThanExpression timeFilter = new GreaterThanExpression( @@ -767,7 +774,7 @@ public class QueryLogicalPlanUtil { new LogicAndExpression(timeFilter, new LogicAndExpression(valueFilter1, valueFilter2)); FilterNode filterNode = new FilterNode( - new PlanNodeId("5"), + queryId.genPlanNodeId(), timeJoinNode, new Expression[] { new TimeSeriesOperand(schemaMap.get("root.sg.d2.s1")), @@ -781,9 +788,14 @@ public class QueryLogicalPlanUtil { AggregationNode aggregationNode = new AggregationNode( - new PlanNodeId("6"), + queryId.genPlanNodeId(), Collections.singletonList(filterNode), Arrays.asList( + new AggregationDescriptor( + AggregationType.COUNT, + AggregationStep.PARTIAL, + Collections.singletonList( + new TimeSeriesOperand(schemaMap.get("root.sg.d2.s1")))), new AggregationDescriptor( AggregationType.COUNT, AggregationStep.PARTIAL, @@ -795,29 +807,24 @@ public class QueryLogicalPlanUtil { Collections.singletonList( new TimeSeriesOperand(schemaMap.get("root.sg.d1.s2")))), new AggregationDescriptor( - AggregationType.LAST_VALUE, + AggregationType.MAX_VALUE, AggregationStep.PARTIAL, Collections.singletonList( - new TimeSeriesOperand(schemaMap.get("root.sg.d1.s1")))), + new TimeSeriesOperand(schemaMap.get("root.sg.d2.s2")))), new AggregationDescriptor( - AggregationType.COUNT, + AggregationType.LAST_VALUE, AggregationStep.PARTIAL, Collections.singletonList( new TimeSeriesOperand(schemaMap.get("root.sg.d2.s1")))), - new AggregationDescriptor( - AggregationType.MAX_VALUE, - AggregationStep.PARTIAL, - Collections.singletonList( - new TimeSeriesOperand(schemaMap.get("root.sg.d2.s2")))), new AggregationDescriptor( AggregationType.LAST_VALUE, AggregationStep.PARTIAL, Collections.singletonList( - new TimeSeriesOperand(schemaMap.get("root.sg.d2.s1")))))); + new TimeSeriesOperand(schemaMap.get("root.sg.d1.s1")))))); GroupByLevelNode groupByLevelNode = new GroupByLevelNode( - new PlanNodeId("7"), + queryId.genPlanNodeId(), Collections.singletonList(aggregationNode), Arrays.asList( new AggregationDescriptor( @@ -865,8 +872,8 @@ public class QueryLogicalPlanUtil { Arrays.asList( "count(root.sg.*.s1)", "max_value(root.sg.*.s2)", "last_value(root.sg.*.s1)")); - OffsetNode offsetNode = new OffsetNode(new PlanNodeId("8"), groupByLevelNode, 100); - LimitNode limitNode = new LimitNode(new PlanNodeId("9"), offsetNode, 100); + OffsetNode offsetNode = new OffsetNode(queryId.genPlanNodeId(), groupByLevelNode, 100); + LimitNode limitNode = new LimitNode(queryId.genPlanNodeId(), offsetNode, 100); querySQLs.add(sql); sqlToPlanMap.put(sql, limitNode); @@ -878,15 +885,16 @@ public class QueryLogicalPlanUtil { "SELECT count(s1), max_value(s2), last_value(s1) FROM root.sg.* WHERE time > 100 and s2 > 10 " + "ORDER BY TIME DESC LIMIT 100 OFFSET 100 ALIGN BY DEVICE"; + QueryId queryId = new QueryId("test"); List sourceNodeList1 = new ArrayList<>(); sourceNodeList1.add( new SeriesScanNode( - new PlanNodeId("0"), + queryId.genPlanNodeId(), (MeasurementPath) schemaMap.get("root.sg.d1.s1"), OrderBy.TIMESTAMP_DESC)); sourceNodeList1.add( new SeriesScanNode( - new PlanNodeId("1"), + queryId.genPlanNodeId(), (MeasurementPath) schemaMap.get("root.sg.d1.s2"), OrderBy.TIMESTAMP_DESC)); sourceNodeList1.forEach( @@ -895,7 +903,7 @@ public class QueryLogicalPlanUtil { }); TimeJoinNode timeJoinNode1 = - new TimeJoinNode(new PlanNodeId("2"), OrderBy.TIMESTAMP_DESC, sourceNodeList1); + new TimeJoinNode(queryId.genPlanNodeId(), OrderBy.TIMESTAMP_DESC, sourceNodeList1); GreaterThanExpression timeFilter = new GreaterThanExpression( @@ -908,7 +916,7 @@ public class QueryLogicalPlanUtil { FilterNode filterNode1 = new FilterNode( - new PlanNodeId("3"), + queryId.genPlanNodeId(), timeJoinNode1, new Expression[] { new TimeSeriesOperand(schemaMap.get("root.sg.d1.s1")), @@ -920,43 +928,41 @@ public class QueryLogicalPlanUtil { AggregationNode aggregationNode1 = new AggregationNode( - new PlanNodeId("4"), + queryId.genPlanNodeId(), Collections.singletonList(filterNode1), Arrays.asList( new AggregationDescriptor( AggregationType.COUNT, - AggregationStep.FINAL, + AggregationStep.SINGLE, Collections.singletonList( new TimeSeriesOperand(schemaMap.get("root.sg.d1.s1")))), new AggregationDescriptor( AggregationType.MAX_VALUE, - AggregationStep.FINAL, + AggregationStep.SINGLE, Collections.singletonList( new TimeSeriesOperand(schemaMap.get("root.sg.d1.s2")))), new AggregationDescriptor( AggregationType.LAST_VALUE, - AggregationStep.FINAL, + AggregationStep.SINGLE, Collections.singletonList( new TimeSeriesOperand(schemaMap.get("root.sg.d1.s1")))))); List sourceNodeList2 = new ArrayList<>(); sourceNodeList2.add( new SeriesScanNode( - new PlanNodeId("5"), + queryId.genPlanNodeId(), (MeasurementPath) schemaMap.get("root.sg.d2.s1"), OrderBy.TIMESTAMP_DESC)); sourceNodeList2.add( new SeriesScanNode( - new PlanNodeId("6"), + queryId.genPlanNodeId(), (MeasurementPath) schemaMap.get("root.sg.d2.s2"), OrderBy.TIMESTAMP_DESC)); sourceNodeList2.forEach( - planNode -> { - ((SeriesScanNode) planNode).setTimeFilter(TimeFilter.gt(100)); - }); + planNode -> ((SeriesScanNode) planNode).setTimeFilter(TimeFilter.gt(100))); TimeJoinNode timeJoinNode2 = - new TimeJoinNode(new PlanNodeId("7"), OrderBy.TIMESTAMP_DESC, sourceNodeList2); + new TimeJoinNode(queryId.genPlanNodeId(), OrderBy.TIMESTAMP_DESC, sourceNodeList2); GreaterThanExpression valueFilter2 = new GreaterThanExpression( @@ -966,7 +972,7 @@ public class QueryLogicalPlanUtil { FilterNode filterNode2 = new FilterNode( - new PlanNodeId("8"), + queryId.genPlanNodeId(), timeJoinNode2, new Expression[] { new TimeSeriesOperand(schemaMap.get("root.sg.d2.s1")), @@ -978,22 +984,22 @@ public class QueryLogicalPlanUtil { AggregationNode aggregationNode2 = new AggregationNode( - new PlanNodeId("9"), + queryId.genPlanNodeId(), Collections.singletonList(filterNode2), Arrays.asList( new AggregationDescriptor( AggregationType.COUNT, - AggregationStep.FINAL, + AggregationStep.SINGLE, Collections.singletonList( new TimeSeriesOperand(schemaMap.get("root.sg.d2.s1")))), new AggregationDescriptor( AggregationType.MAX_VALUE, - AggregationStep.FINAL, + AggregationStep.SINGLE, Collections.singletonList( new TimeSeriesOperand(schemaMap.get("root.sg.d2.s2")))), new AggregationDescriptor( AggregationType.LAST_VALUE, - AggregationStep.FINAL, + AggregationStep.SINGLE, Collections.singletonList( new TimeSeriesOperand(schemaMap.get("root.sg.d2.s1")))))); @@ -1002,7 +1008,7 @@ public class QueryLogicalPlanUtil { deviceToMeasurementIndexesMap.put("root.sg.d2", Arrays.asList(1, 2, 3)); DeviceViewNode deviceViewNode = new DeviceViewNode( - new PlanNodeId("10"), + queryId.genPlanNodeId(), Arrays.asList(OrderBy.DEVICE_ASC, OrderBy.TIMESTAMP_DESC), Arrays.asList( HeaderConstant.COLUMN_DEVICE, "count(s1)", "max_value(s2)", "last_value(s1)"), @@ -1010,8 +1016,8 @@ public class QueryLogicalPlanUtil { deviceViewNode.addChildDeviceNode("root.sg.d1", aggregationNode1); deviceViewNode.addChildDeviceNode("root.sg.d2", aggregationNode2); - OffsetNode offsetNode = new OffsetNode(new PlanNodeId("11"), deviceViewNode, 100); - LimitNode limitNode = new LimitNode(new PlanNodeId("12"), offsetNode, 100); + OffsetNode offsetNode = new OffsetNode(queryId.genPlanNodeId(), deviceViewNode, 100); + LimitNode limitNode = new LimitNode(queryId.genPlanNodeId(), offsetNode, 100); querySQLs.add(sql); sqlToPlanMap.put(sql, limitNode); From 168b1b90ae0d4d90d28916cb713ebd98d146d935 Mon Sep 17 00:00:00 2001 From: CloudWise-Lukemiao <76942485+CloudWise-Lukemiao@users.noreply.github.com> Date: Fri, 20 May 2022 17:56:58 +0800 Subject: [PATCH 061/436] [IOTDB-2621] DockerCompose: Grafana Plugin (#5915) --- .../GrafanaPlugin/Dockerfile-0.14.0-iotdb | 42 ++++++++++++++ .../GrafanaPlugin/docker-compose.yml | 48 +++++++++++++++ .../iotdb/conf/iotdb-rest.properties | 58 +++++++++++++++++++ 3 files changed, 148 insertions(+) create mode 100644 docker/src/main/DockerCompose/GrafanaPlugin/Dockerfile-0.14.0-iotdb create mode 100644 docker/src/main/DockerCompose/GrafanaPlugin/docker-compose.yml create mode 100644 docker/src/main/DockerCompose/GrafanaPlugin/iotdb/conf/iotdb-rest.properties diff --git a/docker/src/main/DockerCompose/GrafanaPlugin/Dockerfile-0.14.0-iotdb b/docker/src/main/DockerCompose/GrafanaPlugin/Dockerfile-0.14.0-iotdb new file mode 100644 index 000000000000..421b7f24caee --- /dev/null +++ b/docker/src/main/DockerCompose/GrafanaPlugin/Dockerfile-0.14.0-iotdb @@ -0,0 +1,42 @@ +# +# 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. +# + +FROM openjdk:11-jre-slim +RUN apt update \ + # procps is for `free` command + && apt install wget unzip lsof procps -y \ + && wget https://downloads.apache.org/iotdb/0.14.0/apache-iotdb-0.14.0-server-bin.zip \ + # if you are in China, use the following URL + #&& wget https://mirrors.tuna.tsinghua.edu.cn/apache/iotdb/0.14.0/apache-iotdb-0.14.0-server-bin.zip \ + && unzip apache-iotdb-0.14.0-server-bin.zip \ + && rm apache-iotdb-0.14.0-server-bin.zip \ + && mv apache-iotdb-0.14.0-server-bin /iotdb \ + && apt remove wget unzip -y \ + && apt autoremove -y \ + && apt purge --auto-remove -y \ + && apt clean -y +EXPOSE 6667 +EXPOSE 31999 +EXPOSE 5555 +EXPOSE 8181 +EXPOSE 18080 +VOLUME /iotdb/data +VOLUME /iotdb/logs +ENV PATH="/iotdb/sbin/:/iotdb/tools/:${PATH}" +ENTRYPOINT ["/iotdb/sbin/start-server.sh"] diff --git a/docker/src/main/DockerCompose/GrafanaPlugin/docker-compose.yml b/docker/src/main/DockerCompose/GrafanaPlugin/docker-compose.yml new file mode 100644 index 000000000000..26a90474f7e1 --- /dev/null +++ b/docker/src/main/DockerCompose/GrafanaPlugin/docker-compose.yml @@ -0,0 +1,48 @@ +# +# 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. +# + +version: "3.7" + +services: + grafana: + image: grafana/grafana:8.2.5 + ports: + - 3000:3000 + environment: + - GF_PLUGINS_ALLOW_LOADING_UNSIGNED_PLUGINS=iotdb + - GF_INSTALL_PLUGINS=https://mirrors.tuna.tsinghua.edu.cn/apache/iotdb/0.14.0/apache-iotdb-0.14.0-grafana-plugin-bin.zip;apache-iotdb-0.14.0-grafana-plugin-bin + iotdb: + image: apache/iotdb0.14 + build: + context: ./ + dockerfile: Dockerfile-0.14.0-iotdb + volumes: + - ./iotdb/conf/iotdb-rest.properties:/iotdb/conf/iotdb-rest.properties + - ./iotdb/data/:/iotdb/data + - ./iotdb/logs:/iotdb/logs + ports: + - 6667:6667 + - 18080:18080 + - 5555:5555 + - 31999:31999 + - 8181:8181 + alertmanager: + image: prom/alertmanager:v0.21.0 + ports: + - 9093:9093 \ No newline at end of file diff --git a/docker/src/main/DockerCompose/GrafanaPlugin/iotdb/conf/iotdb-rest.properties b/docker/src/main/DockerCompose/GrafanaPlugin/iotdb/conf/iotdb-rest.properties new file mode 100644 index 000000000000..75d0ae1b06cb --- /dev/null +++ b/docker/src/main/DockerCompose/GrafanaPlugin/iotdb/conf/iotdb-rest.properties @@ -0,0 +1,58 @@ +# +# 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. +# + +#################### +### REST Service Configuration +#################### + +# Is the REST service enabled +enable_rest_service=true + +# the binding port of the REST service +# rest_service_port=18080 + +# the default row limit to a REST query response when the rowSize parameter is not given in request +# rest_query_default_row_size_limit=10000 + +# the expiration time of the user login information cache (in seconds) +# cache_expire_in_seconds=28800 + +# maximum number of users can be stored in the user login cache. +# cache_max_num=100 + +# init capacity of users can be stored in the user login cache. +# cache_init_num=10 + +# is SSL enabled +# enable_https=false + +# SSL key store path +# key_store_path= + +# SSL key store password +# key_store_pwd= + +# SSL trust store path +# trust_store_path= + +# SSL trust store password. +# trust_store_pwd= + +# SSL timeout (in seconds) +# idle_timeout_in_seconds=50000 From 7cfbda95cef7ab0f4d090d84555f56ece3b8735c Mon Sep 17 00:00:00 2001 From: CloudWise-Lukemiao <76942485+CloudWise-Lukemiao@users.noreply.github.com> Date: Fri, 20 May 2022 17:59:30 +0800 Subject: [PATCH 062/436] [Vulnerability] Fix grafana conncetor SQL injection issue (#5969) --- .../web/grafana/controller/DatabaseConnectController.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/grafana-connector/src/main/java/org/apache/iotdb/web/grafana/controller/DatabaseConnectController.java b/grafana-connector/src/main/java/org/apache/iotdb/web/grafana/controller/DatabaseConnectController.java index 788e983b0950..f13f7d5a4917 100644 --- a/grafana-connector/src/main/java/org/apache/iotdb/web/grafana/controller/DatabaseConnectController.java +++ b/grafana-connector/src/main/java/org/apache/iotdb/web/grafana/controller/DatabaseConnectController.java @@ -111,6 +111,9 @@ public String query(@RequestBody String json) { continue; } String target = object.get(targetStr).getAsString(); + if (target.contains(";")) { + throw new Exception("Only one SQL statement is supported"); + } JsonObject obj = new JsonObject(); obj.addProperty("target", target); String type = getJsonType(object); From 60a47c6093ebbeebec19ce5c0d8472e3e0ab540a Mon Sep 17 00:00:00 2001 From: cmlmakahts <82880298+cmlmakahts@users.noreply.github.com> Date: Fri, 20 May 2022 18:07:48 +0800 Subject: [PATCH 063/436] =?UTF-8?q?[IOTDB-2880]=20Fix=20procedure=20worker?= =?UTF-8?q?=20threads=20do=20not=20await=20correctly=20and=20=E2=80=A6=20(?= =?UTF-8?q?#5968)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [IOTDB-2880] Fix procedure worker threads do not await correctly and clear some useless imports and classes. * [IOTDB-2880] Fix procedure worker threads do not await correctly and clear some useless imports and classes. --- confignode/pom.xml | 5 - .../confignode/persistence/ProcedureInfo.java | 9 +- .../iotdb/confignode/procedure/Procedure.java | 43 ++-- .../procedure/ProcedureExecutor.java | 68 ++---- .../procedure/StateMachineProcedure.java | 4 +- .../procedure/conf/ProcedureNodeConfig.java | 155 ------------- .../conf/ProcedureNodeConfigDescriptor.java | 219 ------------------ .../procedure/conf/ProcedureNodeConstant.java | 32 --- .../procedure/store/ConfigProcedureStore.java | 6 +- .../procedure/store/IProcedureStore.java | 4 +- .../procedure/store/ProcedureFactory.java | 5 +- .../procedure/store/ProcedureStore.java | 14 +- .../procedure/NoopProcedureStore.java | 4 +- .../procedure/TestProcedureBase.java | 7 +- .../procedure/TestProcedureExecutor.java | 2 - .../procedure/store/TestProcedureStore.java | 9 +- 16 files changed, 64 insertions(+), 522 deletions(-) delete mode 100644 confignode/src/main/java/org/apache/iotdb/confignode/procedure/conf/ProcedureNodeConfig.java delete mode 100644 confignode/src/main/java/org/apache/iotdb/confignode/procedure/conf/ProcedureNodeConfigDescriptor.java delete mode 100644 confignode/src/main/java/org/apache/iotdb/confignode/procedure/conf/ProcedureNodeConstant.java diff --git a/confignode/pom.xml b/confignode/pom.xml index 875d23250956..7f1ab3ff9694 100644 --- a/confignode/pom.xml +++ b/confignode/pom.xml @@ -50,11 +50,6 @@ iotdb-server ${project.version} - - org.apache.iotdb - iotdb-procedure - ${project.version} - junit junit diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/ProcedureInfo.java b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/ProcedureInfo.java index 62db45719d90..e12e3ae5a1bd 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/ProcedureInfo.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/ProcedureInfo.java @@ -24,8 +24,8 @@ import org.apache.iotdb.confignode.consensus.request.write.DeleteProcedureReq; import org.apache.iotdb.confignode.consensus.request.write.UpdateProcedureReq; import org.apache.iotdb.confignode.procedure.Procedure; -import org.apache.iotdb.confignode.procedure.conf.ProcedureNodeConstant; import org.apache.iotdb.confignode.procedure.store.ProcedureFactory; +import org.apache.iotdb.confignode.procedure.store.ProcedureStore; import org.apache.iotdb.confignode.procedure.store.ProcedureWAL; import org.apache.iotdb.rpc.TSStatusCode; @@ -52,10 +52,7 @@ public void load(List procedureList) { try { Files.list(Paths.get(procedureWalDir)) .filter( - path -> - path.getFileName() - .toString() - .endsWith(ProcedureNodeConstant.PROCEDURE_WAL_SUFFIX)) + path -> path.getFileName().toString().endsWith(ProcedureStore.PROCEDURE_WAL_SUFFIX)) .sorted( (p1, p2) -> Long.compareUnsigned( @@ -78,7 +75,7 @@ public void load(List procedureList) { public TSStatus updateProcedure(UpdateProcedureReq updateProcedureReq) { Procedure procedure = updateProcedureReq.getProcedure(); long procId = procedure.getProcId(); - Path path = Paths.get(procedureWalDir, procId + ProcedureNodeConstant.PROCEDURE_WAL_SUFFIX); + Path path = Paths.get(procedureWalDir, procId + ProcedureStore.PROCEDURE_WAL_SUFFIX); ProcedureWAL procedureWAL = procWALMap.computeIfAbsent(procId, id -> new ProcedureWAL(path, procedureFactory)); try { diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/procedure/Procedure.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/Procedure.java index 616bbe90ac5f..9561ab177c80 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/procedure/Procedure.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/Procedure.java @@ -95,17 +95,14 @@ public final boolean hasLock() { * @param env the environment passed to the ProcedureExecutor * @return a set of sub-procedures to run or ourselves if there is more work to do or null if the * procedure is done. - * @throws org.apache.iotdb.confignode.procedure.exception.ProcedureYieldException the procedure - * will be added back to the queue and retried later. + * @throws ProcedureYieldException the procedure will be added back to the queue and retried + * later. * @throws InterruptedException the procedure will be added back to the queue and retried later. - * @throws org.apache.iotdb.confignode.procedure.exception.ProcedureSuspendedException Signal to - * the executor that Procedure has suspended itself and has set itself up waiting for an - * external event to wake it back up again. + * @throws ProcedureSuspendedException Signal to the executor that Procedure has suspended itself + * and has set itself up waiting for an external event to wake it back up again. */ protected abstract Procedure[] execute(Env env) - throws org.apache.iotdb.confignode.procedure.exception.ProcedureYieldException, - org.apache.iotdb.confignode.procedure.exception.ProcedureSuspendedException, - InterruptedException; + throws ProcedureYieldException, ProcedureSuspendedException, InterruptedException; /** * The code to undo what was done by the execute() code. It is called when the procedure or one of @@ -214,17 +211,16 @@ public void deserialize(ByteBuffer byteBuffer) { byteBuffer.get(messageBytes); errMsg = new String(messageBytes, StandardCharsets.UTF_8); } - org.apache.iotdb.confignode.procedure.exception.ProcedureException exception; + ProcedureException exception; try { exception = - (org.apache.iotdb.confignode.procedure.exception.ProcedureException) - exceptionClass.getConstructor(String.class).newInstance(errMsg); + (ProcedureException) exceptionClass.getConstructor(String.class).newInstance(errMsg); } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { LOG.warn("Instantiation exception class failed", e); - exception = new org.apache.iotdb.confignode.procedure.exception.ProcedureException(errMsg); + exception = new ProcedureException(errMsg); } setFailure(exception); @@ -290,8 +286,8 @@ protected boolean waitInitialized(Env env) { * @param env environment * @return state of lock */ - protected org.apache.iotdb.confignode.procedure.state.ProcedureLockState acquireLock(Env env) { - return org.apache.iotdb.confignode.procedure.state.ProcedureLockState.LOCK_ACQUIRED; + protected ProcedureLockState acquireLock(Env env) { + return ProcedureLockState.LOCK_ACQUIRED; } /** @@ -359,9 +355,7 @@ protected boolean isYieldAfterExecution(Env env) { * @return sub procedures */ protected Procedure[] doExecute(Env env) - throws ProcedureYieldException, - org.apache.iotdb.confignode.procedure.exception.ProcedureSuspendedException, - InterruptedException { + throws ProcedureYieldException, ProcedureSuspendedException, InterruptedException { try { updateTimestamp(); return execute(env); @@ -393,17 +387,16 @@ public void doRollback(Env env) throws IOException, InterruptedException { * @param store ProcedureStore * @return ProcedureLockState */ - public final org.apache.iotdb.confignode.procedure.state.ProcedureLockState doAcquireLock( - Env env, IProcedureStore store) { + public final ProcedureLockState doAcquireLock(Env env, IProcedureStore store) { if (waitInitialized(env)) { - return org.apache.iotdb.confignode.procedure.state.ProcedureLockState.LOCK_EVENT_WAIT; + return ProcedureLockState.LOCK_EVENT_WAIT; } if (lockedWhenLoading) { lockedWhenLoading = false; locked = true; - return org.apache.iotdb.confignode.procedure.state.ProcedureLockState.LOCK_ACQUIRED; + return ProcedureLockState.LOCK_ACQUIRED; } - org.apache.iotdb.confignode.procedure.state.ProcedureLockState state = acquireLock(env); + ProcedureLockState state = acquireLock(env); if (state == ProcedureLockState.LOCK_ACQUIRED) { locked = true; store.update(this); @@ -736,12 +729,10 @@ public synchronized ProcedureState getState() { } protected void setFailure(final String source, final Throwable cause) { - setFailure( - new org.apache.iotdb.confignode.procedure.exception.ProcedureException(source, cause)); + setFailure(new ProcedureException(source, cause)); } - protected synchronized void setFailure( - final org.apache.iotdb.confignode.procedure.exception.ProcedureException exception) { + protected synchronized void setFailure(final ProcedureException exception) { this.exception = exception; if (!isFinished()) { setState(ProcedureState.FAILED); diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/procedure/ProcedureExecutor.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/ProcedureExecutor.java index 2b23bcedf6bf..5719a351ac46 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/procedure/ProcedureExecutor.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/ProcedureExecutor.java @@ -65,15 +65,14 @@ public class ProcedureExecutor { private CopyOnWriteArrayList workerThreads; - private org.apache.iotdb.confignode.procedure.TimeoutExecutorThread timeoutExecutor; + private TimeoutExecutorThread timeoutExecutor; - private org.apache.iotdb.confignode.procedure.TimeoutExecutorThread workerMonitorExecutor; + private TimeoutExecutorThread workerMonitorExecutor; private int corePoolSize; private int maxPoolSize; - private volatile long keepAliveTime; - private final org.apache.iotdb.confignode.procedure.scheduler.ProcedureScheduler scheduler; + private final ProcedureScheduler scheduler; private final AtomicLong lastProcId = new AtomicLong(-1); private final AtomicLong workId = new AtomicLong(0); @@ -83,9 +82,7 @@ public class ProcedureExecutor { private final IProcedureStore store; public ProcedureExecutor( - final Env environment, - final IProcedureStore store, - final org.apache.iotdb.confignode.procedure.scheduler.ProcedureScheduler scheduler) { + final Env environment, final IProcedureStore store, final ProcedureScheduler scheduler) { this.environment = environment; this.scheduler = scheduler; this.store = store; @@ -101,8 +98,7 @@ public void init(int numThreads) { this.maxPoolSize = 10 * numThreads; this.threadGroup = new ThreadGroup("ProcedureWorkerGroup"); this.timeoutExecutor = - new org.apache.iotdb.confignode.procedure.TimeoutExecutorThread<>( - this, threadGroup, "ProcedureTimeoutExecutor"); + new TimeoutExecutorThread<>(this, threadGroup, "ProcedureTimeoutExecutor"); this.workerMonitorExecutor = new TimeoutExecutorThread<>(this, threadGroup, "ProcedureWorkerThreadMonitor"); workId.set(0); @@ -117,15 +113,6 @@ public void init(int numThreads) { recover(); } - public void setKeepAliveTime(final long keepAliveTime, final TimeUnit timeUnit) { - this.keepAliveTime = timeUnit.toMillis(keepAliveTime); - this.scheduler.signalAll(); - } - - public long getKeepAliveTime(final TimeUnit timeUnit) { - return timeUnit.convert(keepAliveTime, TimeUnit.MILLISECONDS); - } - private void recover() { // 1.Build rollback stack int runnableCount = 0; @@ -383,7 +370,7 @@ private void executeProcedure(Procedure proc) { } break; } - org.apache.iotdb.confignode.procedure.state.ProcedureLockState lockState = acquireLock(proc); + ProcedureLockState lockState = acquireLock(proc); switch (lockState) { case LOCK_ACQUIRED: executeProcedure(rootProcStack, proc); @@ -442,9 +429,7 @@ private void executeProcedure(RootProcedureStack rootProcStack, Procedure p yieldProcedure(proc); } catch (Throwable e) { LOG.error("CODE-BUG:{}", proc, e); - proc.setFailure( - new org.apache.iotdb.confignode.procedure.exception.ProcedureException( - e.getMessage(), e)); + proc.setFailure(new ProcedureException(e.getMessage(), e)); } if (!proc.isFailed()) { @@ -555,9 +540,7 @@ private Procedure[] initializeChildren( Procedure subproc = subprocs[i]; if (subproc == null) { String errMsg = "subproc[" + i + "] is null, aborting procedure"; - proc.setFailure( - new org.apache.iotdb.confignode.procedure.exception.ProcedureException( - (errMsg), new IllegalArgumentException(errMsg))); + proc.setFailure(new ProcedureException((errMsg), new IllegalArgumentException(errMsg))); return null; } subproc.setParentProcId(proc.getProcId()); @@ -613,24 +596,21 @@ private ProcedureLockState executeRootStackRollback( cleanupAfterRollback(procedure); continue; } - org.apache.iotdb.confignode.procedure.state.ProcedureLockState lockState = - acquireLock(procedure); - if (lockState - != org.apache.iotdb.confignode.procedure.state.ProcedureLockState.LOCK_ACQUIRED) { + ProcedureLockState lockState = acquireLock(procedure); + if (lockState != ProcedureLockState.LOCK_ACQUIRED) { return lockState; } lockState = executeRollback(procedure); releaseLock(procedure, false); - boolean abortRollback = - lockState != org.apache.iotdb.confignode.procedure.state.ProcedureLockState.LOCK_ACQUIRED; + boolean abortRollback = lockState != ProcedureLockState.LOCK_ACQUIRED; abortRollback |= !isRunning() || !store.isRunning(); if (abortRollback) { return lockState; } if (!procedure.isFinished() && procedure.isYieldAfterExecution(this.environment)) { - return org.apache.iotdb.confignode.procedure.state.ProcedureLockState.LOCK_YIELD_WAIT; + return ProcedureLockState.LOCK_YIELD_WAIT; } if (procedure != rootProcedure) { @@ -640,13 +620,12 @@ private ProcedureLockState executeRootStackRollback( LOG.info("Rolled back {}, time duration is {}", rootProcedure, rootProcedure.elapsedTime()); rootProcedureCleanup(rootProcedure); - return org.apache.iotdb.confignode.procedure.state.ProcedureLockState.LOCK_ACQUIRED; + return ProcedureLockState.LOCK_ACQUIRED; } - private org.apache.iotdb.confignode.procedure.state.ProcedureLockState acquireLock( - Procedure proc) { + private ProcedureLockState acquireLock(Procedure proc) { if (proc.hasLock()) { - return org.apache.iotdb.confignode.procedure.state.ProcedureLockState.LOCK_ACQUIRED; + return ProcedureLockState.LOCK_ACQUIRED; } return proc.doAcquireLock(this.environment, store); } @@ -658,8 +637,7 @@ private org.apache.iotdb.confignode.procedure.state.ProcedureLockState acquireLo * @param procedure procedure * @return procedure lock state */ - private org.apache.iotdb.confignode.procedure.state.ProcedureLockState executeRollback( - Procedure procedure) { + private ProcedureLockState executeRollback(Procedure procedure) { ReentrantLock idLock = idLockMap.computeIfAbsent(procedure.getProcId(), procId -> new ReentrantLock()); try { @@ -675,7 +653,7 @@ private org.apache.iotdb.confignode.procedure.state.ProcedureLockState executeRo idLock.unlock(); } cleanupAfterRollback(procedure); - return org.apache.iotdb.confignode.procedure.state.ProcedureLockState.LOCK_ACQUIRED; + return ProcedureLockState.LOCK_ACQUIRED; } private void cleanupAfterRollback(Procedure procedure) { @@ -740,6 +718,7 @@ private long pushProcedure(Procedure procedure) { private class WorkerThread extends StoppableThread { private final AtomicLong startTime = new AtomicLong(Long.MAX_VALUE); private volatile Procedure activeProcedure; + protected long keepAliveTime = -1; public WorkerThread(ThreadGroup threadGroup) { this(threadGroup, "ProcExecWorker-"); @@ -806,8 +785,10 @@ public long getCurrentRunTime() { // A worker thread which can be added when core workers are stuck. Will timeout after // keepAliveTime if there is no procedure to run. private final class KeepAliveWorkerThread extends WorkerThread { + public KeepAliveWorkerThread(ThreadGroup group) { super(group, "KAProcExecWorker-"); + this.keepAliveTime = TimeUnit.SECONDS.toMillis(10); } @Override @@ -823,10 +804,6 @@ private final class WorkerMonitor extends InternalProcedure { private static final float DEFAULT_WORKER_ADD_STUCK_PERCENTAGE = 0.5f; // 50% stuck - private float addWorkerStuckPercentage = DEFAULT_WORKER_ADD_STUCK_PERCENTAGE; - private int timeoutInterval = DEFAULT_WORKER_MONITOR_INTERVAL; - private int stuckThreshold = DEFAULT_WORKER_STUCK_THRESHOLD; - public WorkerMonitor() { super(DEFAULT_WORKER_MONITOR_INTERVAL); updateTimestamp(); @@ -836,7 +813,8 @@ private int checkForStuckWorkers() { // check if any of the worker is stuck int stuckCount = 0; for (WorkerThread worker : workerThreads) { - if (worker.activeProcedure == null || worker.getCurrentRunTime() < stuckThreshold) { + if (worker.activeProcedure == null + || worker.getCurrentRunTime() < DEFAULT_WORKER_STUCK_THRESHOLD) { continue; } @@ -857,7 +835,7 @@ private void checkThreadCount(final int stuckCount) { final float stuckPerc = ((float) stuckCount) / workerThreads.size(); // let's add new worker thread more aggressively, as they will timeout finally if there is no // work to do. - if (stuckPerc >= addWorkerStuckPercentage && workerThreads.size() < maxPoolSize) { + if (stuckPerc >= DEFAULT_WORKER_ADD_STUCK_PERCENTAGE && workerThreads.size() < maxPoolSize) { final KeepAliveWorkerThread worker = new KeepAliveWorkerThread(threadGroup); workerThreads.add(worker); worker.start(); diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/procedure/StateMachineProcedure.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/StateMachineProcedure.java index 6d05b9f73326..2f5775cd9198 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/procedure/StateMachineProcedure.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/StateMachineProcedure.java @@ -78,9 +78,7 @@ public enum Flow { * another step. */ protected abstract Flow executeFromState(Env env, TState state) - throws org.apache.iotdb.confignode.procedure.exception.ProcedureSuspendedException, - org.apache.iotdb.confignode.procedure.exception.ProcedureYieldException, - InterruptedException; + throws ProcedureSuspendedException, ProcedureYieldException, InterruptedException; /** * called to perform the rollback of the specified state diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/procedure/conf/ProcedureNodeConfig.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/conf/ProcedureNodeConfig.java deleted file mode 100644 index 4b26873500e7..000000000000 --- a/confignode/src/main/java/org/apache/iotdb/confignode/procedure/conf/ProcedureNodeConfig.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * 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.iotdb.confignode.procedure.conf; - -import java.io.File; - -public class ProcedureNodeConfig { - - private String rpcAddress = "0.0.0.0"; - private int rpcPort = 22281; - private int confignodePort = 22277; - private int datanodePort = 22278; - private int rpcMaxConcurrentClientNum = 65535; - private boolean rpcAdvancedCompressionEnable = false; - private boolean isRpcThriftCompressionEnabled = false; - private int thriftMaxFrameSize = 536870912; - private int thriftDefaultBufferSize = 1024; - private int thriftServerAwaitTimeForStopService = 60; - private String procedureWalDir = - org.apache.iotdb.confignode.procedure.conf.ProcedureNodeConstant.PROC_DIR - + File.separator - + ProcedureNodeConstant.WAL_DIR; - private int completedEvictTTL = 800; - private int completedCleanInterval = 30; - private int workerThreadsCoreSize = Math.max(Runtime.getRuntime().availableProcessors() / 4, 16); - - public String getRpcAddress() { - return rpcAddress; - } - - public void setRpcAddress(String rpcAddress) { - this.rpcAddress = rpcAddress; - } - - public int getRpcPort() { - return rpcPort; - } - - public void setRpcPort(int rpcPort) { - this.rpcPort = rpcPort; - } - - public int getConfignodePort() { - return confignodePort; - } - - public void setConfignodePort(int confignodePort) { - this.confignodePort = confignodePort; - } - - public int getDatanodePort() { - return datanodePort; - } - - public void setDatanodePort(int datanodePort) { - this.datanodePort = datanodePort; - } - - public int getRpcMaxConcurrentClientNum() { - return rpcMaxConcurrentClientNum; - } - - public void setRpcMaxConcurrentClientNum(int rpcMaxConcurrentClientNum) { - this.rpcMaxConcurrentClientNum = rpcMaxConcurrentClientNum; - } - - public boolean isRpcThriftCompressionEnabled() { - return isRpcThriftCompressionEnabled; - } - - public void setRpcThriftCompressionEnabled(boolean rpcThriftCompressionEnabled) { - isRpcThriftCompressionEnabled = rpcThriftCompressionEnabled; - } - - public int getThriftMaxFrameSize() { - return thriftMaxFrameSize; - } - - public void setThriftMaxFrameSize(int thriftMaxFrameSize) { - this.thriftMaxFrameSize = thriftMaxFrameSize; - } - - public int getThriftDefaultBufferSize() { - return thriftDefaultBufferSize; - } - - public void setThriftDefaultBufferSize(int thriftDefaultBufferSize) { - this.thriftDefaultBufferSize = thriftDefaultBufferSize; - } - - public int getThriftServerAwaitTimeForStopService() { - return thriftServerAwaitTimeForStopService; - } - - public void setThriftServerAwaitTimeForStopService(int thriftServerAwaitTimeForStopService) { - this.thriftServerAwaitTimeForStopService = thriftServerAwaitTimeForStopService; - } - - public String getProcedureWalDir() { - return procedureWalDir; - } - - public void setProcedureWalDir(String procedureWalDir) { - this.procedureWalDir = procedureWalDir; - } - - public int getCompletedEvictTTL() { - return completedEvictTTL; - } - - public void setCompletedEvictTTL(int completedEvictTTL) { - this.completedEvictTTL = completedEvictTTL; - } - - public int getCompletedCleanInterval() { - return completedCleanInterval; - } - - public void setCompletedCleanInterval(int completedCleanInterval) { - this.completedCleanInterval = completedCleanInterval; - } - - public boolean isRpcAdvancedCompressionEnable() { - return rpcAdvancedCompressionEnable; - } - - public void setRpcAdvancedCompressionEnable(boolean rpcAdvancedCompressionEnable) { - this.rpcAdvancedCompressionEnable = rpcAdvancedCompressionEnable; - } - - public int getWorkerThreadsCoreSize() { - return workerThreadsCoreSize; - } - - public void setWorkerThreadsCoreSize(int workerThreadsCoreSize) { - this.workerThreadsCoreSize = workerThreadsCoreSize; - } -} diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/procedure/conf/ProcedureNodeConfigDescriptor.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/conf/ProcedureNodeConfigDescriptor.java deleted file mode 100644 index 73ce5651cd1d..000000000000 --- a/confignode/src/main/java/org/apache/iotdb/confignode/procedure/conf/ProcedureNodeConfigDescriptor.java +++ /dev/null @@ -1,219 +0,0 @@ -/* - * 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.iotdb.confignode.procedure.conf; - -import org.apache.iotdb.commons.exception.StartupException; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Properties; - -public class ProcedureNodeConfigDescriptor { - private static final Logger LOG = LoggerFactory.getLogger(ProcedureNodeConfigDescriptor.class); - - private final ProcedureNodeConfig conf = new ProcedureNodeConfig(); - - private ProcedureNodeConfigDescriptor() { - loadProps(); - } - - public ProcedureNodeConfig getConf() { - return conf; - } - - /** - * get props url location - * - * @return url object if location exit, otherwise null. - */ - public URL getPropsUrl() { - // Check if a config-directory was specified first. - String urlString = System.getProperty(ProcedureNodeConstant.PROCEDURE_CONF_DIR, null); - // If it wasn't, check if a home directory was provided - if (urlString == null) { - urlString = System.getProperty(ProcedureNodeConstant.PROCEDURENODE_HOME, null); - if (urlString != null) { - urlString = - urlString - + File.separatorChar - + "conf" - + File.separatorChar - + ProcedureNodeConstant.CONF_NAME; - } else { - // When start ProcedureNode with the script, the environment variables ProcedureNode_CONF - // and ProcedureNode_HOME will be set. But we didn't set these two in developer mode. - // Thus, just return null and use default Configuration in developer mode. - return null; - } - } - // If a config location was provided, but it doesn't end with a properties file, - // append the default location. - else if (!urlString.endsWith(".properties")) { - urlString += (File.separatorChar + ProcedureNodeConstant.CONF_NAME); - } - - // If the url doesn't start with "file:" or "classpath:", it's provided as a no path. - // So we need to add it to make it a real URL. - if (!urlString.startsWith("file:") && !urlString.startsWith("classpath:")) { - urlString = "file:" + urlString; - } - try { - return new URL(urlString); - } catch (MalformedURLException e) { - return null; - } - } - - private void loadProps() { - URL url = getPropsUrl(); - if (url == null) { - LOG.warn( - "Couldn't load the ProcedureNode configuration from any of the known sources. Use default configuration."); - return; - } - - try (InputStream inputStream = url.openStream()) { - - LOG.info("start reading ProcedureNode conf file: {}", url); - - Properties properties = new Properties(); - properties.load(inputStream); - - conf.setRpcAddress( - properties.getProperty("procedure_node_address", String.valueOf(conf.getRpcAddress()))); - - conf.setRpcPort( - Integer.parseInt( - properties.getProperty("config_node_rpc_port", String.valueOf(conf.getRpcPort())))); - - conf.setConfignodePort( - Integer.parseInt( - properties.getProperty( - "config_node_port", String.valueOf(conf.getConfignodePort())))); - - conf.setDatanodePort( - Integer.parseInt( - properties.getProperty("date_node_port", String.valueOf(conf.getDatanodePort())))); - - conf.setRpcAdvancedCompressionEnable( - Boolean.parseBoolean( - properties.getProperty( - "rpc_advanced_compression_enable", - String.valueOf(conf.isRpcAdvancedCompressionEnable())))); - - conf.setRpcThriftCompressionEnabled( - Boolean.parseBoolean( - properties.getProperty( - "rpc_thrift_compression_enable", - String.valueOf(conf.isRpcThriftCompressionEnabled())))); - - conf.setRpcMaxConcurrentClientNum( - Integer.parseInt( - properties.getProperty( - "rpc_max_concurrent_client_num", - String.valueOf(conf.getRpcMaxConcurrentClientNum())))); - - conf.setThriftDefaultBufferSize( - Integer.parseInt( - properties.getProperty( - "thrift_init_buffer_size", String.valueOf(conf.getThriftDefaultBufferSize())))); - - conf.setThriftMaxFrameSize( - Integer.parseInt( - properties.getProperty( - "thrift_max_frame_size", String.valueOf(conf.getThriftMaxFrameSize())))); - - conf.setProcedureWalDir(properties.getProperty("proc_wal_dir", conf.getProcedureWalDir())); - - conf.setCompletedEvictTTL( - Integer.parseInt( - properties.getProperty( - "completed_evict_ttl", String.valueOf(conf.getCompletedEvictTTL())))); - - conf.setCompletedCleanInterval( - Integer.parseInt( - properties.getProperty( - "completed_clean_interval", String.valueOf(conf.getCompletedCleanInterval())))); - - conf.setWorkerThreadsCoreSize( - Integer.parseInt( - properties.getProperty( - "workerthreads_core_size", String.valueOf(conf.getWorkerThreadsCoreSize())))); - } catch (IOException e) { - LOG.warn("Couldn't load ProcedureNode conf file, use default config", e); - } finally { - updatePath(); - } - } - - private void updatePath() { - formulateFolders(); - } - - private void formulateFolders() { - conf.setProcedureWalDir(addHomeDir(conf.getProcedureWalDir())); - } - - private String addHomeDir(String dir) { - String homeDir = System.getProperty(ProcedureNodeConstant.PROCEDURENODE_HOME, null); - if (!new File(dir).isAbsolute() && homeDir != null && homeDir.length() > 0) { - if (!homeDir.endsWith(File.separator)) { - dir = homeDir + File.separatorChar + dir; - } else { - dir = homeDir + dir; - } - } - return dir; - } - - public static ProcedureNodeConfigDescriptor getInstance() { - return ProcedureNodeDescriptorHolder.INSTANCE; - } - - public void checkConfig() throws StartupException { - File walDir = new File(conf.getProcedureWalDir()); - if (!walDir.exists()) { - if (walDir.mkdirs()) { - LOG.info("Make procedure wall dirs:{}", walDir); - } else { - throw new StartupException( - String.format( - "Start procedure node failed, because can not make wal dirs:%s.", - walDir.getAbsolutePath())); - } - } - } - - private static class ProcedureNodeDescriptorHolder { - - private static final ProcedureNodeConfigDescriptor INSTANCE = - new ProcedureNodeConfigDescriptor(); - - private ProcedureNodeDescriptorHolder() { - // empty constructor - } - } -} diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/procedure/conf/ProcedureNodeConstant.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/conf/ProcedureNodeConstant.java deleted file mode 100644 index fd148a14f100..000000000000 --- a/confignode/src/main/java/org/apache/iotdb/confignode/procedure/conf/ProcedureNodeConstant.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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.iotdb.confignode.procedure.conf; - -public class ProcedureNodeConstant { - public static final String PROCEDURE_WAL_SUFFIX = ".proc.wal"; - public static final String PROCEDURENODE_PACKAGE = "org.apache.iotdb.procedurenode.service"; - public static final String JMX_TYPE = "type"; - public static final String GLOBAL_NAME = "IoTDB ProcedureNode"; - public static final String PROCEDURE_CONF_DIR = "PROCEDURENODE_CONF"; - public static final String PROCEDURENODE_HOME = "PROCEDURENODE_HOME"; - public static final String CONF_NAME = "iotdb-procedure.properties"; - public static final String PROC_DIR = "proc"; - public static final String WAL_DIR = "wal"; -} diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/procedure/store/ConfigProcedureStore.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/store/ConfigProcedureStore.java index a6c2512e6b2e..d21b250b31e1 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/procedure/store/ConfigProcedureStore.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/store/ConfigProcedureStore.java @@ -69,12 +69,12 @@ public void setRunning(boolean running) { } @Override - public void load(List procedureList) { + public void load(List procedureList) { procedureInfo.load(procedureList); } @Override - public void update(org.apache.iotdb.confignode.procedure.Procedure procedure) { + public void update(Procedure procedure) { UpdateProcedureReq updateProcedureReq = new UpdateProcedureReq(); ProcedureFactory.ProcedureType procedureType = ProcedureFactory.getProcedureType(procedure); if (procedureType != null) { @@ -84,7 +84,7 @@ public void update(org.apache.iotdb.confignode.procedure.Procedure procedure) { } @Override - public void update(org.apache.iotdb.confignode.procedure.Procedure[] subprocs) { + public void update(Procedure[] subprocs) { for (Procedure subproc : subprocs) { update(subproc); } diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/procedure/store/IProcedureStore.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/store/IProcedureStore.java index 3e9542f0fc8c..79161e978408 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/procedure/store/IProcedureStore.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/store/IProcedureStore.java @@ -29,9 +29,9 @@ public interface IProcedureStore { void setRunning(boolean running); - void load(List procedureList); + void load(List procedureList); - void update(org.apache.iotdb.confignode.procedure.Procedure procedure); + void update(Procedure procedure); void update(Procedure[] subprocs); diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/procedure/store/ProcedureFactory.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/store/ProcedureFactory.java index 907a34c369dc..6753d304b2c1 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/procedure/store/ProcedureFactory.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/store/ProcedureFactory.java @@ -28,14 +28,13 @@ public class ProcedureFactory implements IProcedureFactory { @Override - public org.apache.iotdb.confignode.procedure.Procedure create(ByteBuffer buffer) - throws IOException { + public Procedure create(ByteBuffer buffer) throws IOException { int typeNum = buffer.getInt(); if (typeNum >= ProcedureType.values().length) { throw new IOException("unrecognized log type " + typeNum); } ProcedureType type = ProcedureType.values()[typeNum]; - org.apache.iotdb.confignode.procedure.Procedure procedure; + Procedure procedure; switch (type) { case DELETE_STORAGE_GROUP_PROCEDURE: procedure = new DeleteStorageGroupProcedure(); diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/procedure/store/ProcedureStore.java b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/store/ProcedureStore.java index 29e4a39d64b6..9700675728a8 100644 --- a/confignode/src/main/java/org/apache/iotdb/confignode/procedure/store/ProcedureStore.java +++ b/confignode/src/main/java/org/apache/iotdb/confignode/procedure/store/ProcedureStore.java @@ -19,10 +19,9 @@ package org.apache.iotdb.confignode.procedure.store; +import org.apache.iotdb.commons.conf.CommonDescriptor; import org.apache.iotdb.commons.utils.TestOnly; import org.apache.iotdb.confignode.procedure.Procedure; -import org.apache.iotdb.confignode.procedure.conf.ProcedureNodeConfigDescriptor; -import org.apache.iotdb.confignode.procedure.conf.ProcedureNodeConstant; import org.apache.commons.io.FileUtils; import org.slf4j.Logger; @@ -40,8 +39,9 @@ public class ProcedureStore implements IProcedureStore { private static final Logger LOG = LoggerFactory.getLogger(ProcedureStore.class); private String procedureWalDir = - ProcedureNodeConfigDescriptor.getInstance().getConf().getProcedureWalDir(); + CommonDescriptor.getInstance().getConfig().getProcedureWalFolder(); private final ConcurrentHashMap procWALMap = new ConcurrentHashMap<>(); + public static final String PROCEDURE_WAL_SUFFIX = ".proc.wal"; private final IProcedureFactory procedureFactory; private volatile boolean isRunning = false; @@ -81,11 +81,7 @@ public void setRunning(boolean running) { public void load(List procedureList) { try { Files.list(Paths.get(procedureWalDir)) - .filter( - path -> - path.getFileName() - .toString() - .endsWith(ProcedureNodeConstant.PROCEDURE_WAL_SUFFIX)) + .filter(path -> path.getFileName().toString().endsWith(PROCEDURE_WAL_SUFFIX)) .sorted( (p1, p2) -> Long.compareUnsigned( @@ -116,7 +112,7 @@ public void update(Procedure procedure) { return; } long procId = procedure.getProcId(); - Path path = Paths.get(procedureWalDir, procId + ProcedureNodeConstant.PROCEDURE_WAL_SUFFIX); + Path path = Paths.get(procedureWalDir, procId + ProcedureStore.PROCEDURE_WAL_SUFFIX); ProcedureWAL procedureWAL = procWALMap.computeIfAbsent(procId, id -> new ProcedureWAL(path, procedureFactory)); try { diff --git a/confignode/src/test/java/org/apache/iotdb/confignode/procedure/NoopProcedureStore.java b/confignode/src/test/java/org/apache/iotdb/confignode/procedure/NoopProcedureStore.java index 37464a5020d0..07e35a6f2695 100644 --- a/confignode/src/test/java/org/apache/iotdb/confignode/procedure/NoopProcedureStore.java +++ b/confignode/src/test/java/org/apache/iotdb/confignode/procedure/NoopProcedureStore.java @@ -38,10 +38,10 @@ public void setRunning(boolean running) { } @Override - public void load(List procedureList) {} + public void load(List procedureList) {} @Override - public void update(org.apache.iotdb.confignode.procedure.Procedure procedure) {} + public void update(Procedure procedure) {} @Override public void update(Procedure[] subprocs) {} diff --git a/confignode/src/test/java/org/apache/iotdb/confignode/procedure/TestProcedureBase.java b/confignode/src/test/java/org/apache/iotdb/confignode/procedure/TestProcedureBase.java index 3176426feb39..fe630681efab 100644 --- a/confignode/src/test/java/org/apache/iotdb/confignode/procedure/TestProcedureBase.java +++ b/confignode/src/test/java/org/apache/iotdb/confignode/procedure/TestProcedureBase.java @@ -32,7 +32,7 @@ public class TestProcedureBase { protected TestProcEnv env; protected IProcedureStore procStore; - protected org.apache.iotdb.confignode.procedure.ProcedureExecutor procExecutor; + protected ProcedureExecutor procExecutor; @Before public void setUp() { @@ -52,8 +52,7 @@ public void tearDown() { protected void initExecutor() { this.env = new TestProcEnv(); this.procStore = new NoopProcedureStore(); - this.procExecutor = - new org.apache.iotdb.confignode.procedure.ProcedureExecutor<>(env, procStore); + this.procExecutor = new ProcedureExecutor<>(env, procStore); this.env.setScheduler(this.procExecutor.getScheduler()); this.procExecutor.init(4); } @@ -74,7 +73,7 @@ public void setProcStore(IProcedureStore procStore) { this.procStore = procStore; } - public org.apache.iotdb.confignode.procedure.ProcedureExecutor getProcExecutor() { + public ProcedureExecutor getProcExecutor() { return procExecutor; } diff --git a/confignode/src/test/java/org/apache/iotdb/confignode/procedure/TestProcedureExecutor.java b/confignode/src/test/java/org/apache/iotdb/confignode/procedure/TestProcedureExecutor.java index f007e7dce39d..aa35a33e56fc 100644 --- a/confignode/src/test/java/org/apache/iotdb/confignode/procedure/TestProcedureExecutor.java +++ b/confignode/src/test/java/org/apache/iotdb/confignode/procedure/TestProcedureExecutor.java @@ -54,7 +54,6 @@ public void testSubmitProcedure() { @Test public void testWorkerThreadStuck() throws InterruptedException { - procExecutor.setKeepAliveTime(10, TimeUnit.SECONDS); Semaphore latch1 = new Semaphore(2); latch1.acquire(2); StuckProcedure busyProc1 = new StuckProcedure(latch1); @@ -83,7 +82,6 @@ public void testWorkerThreadStuck() throws InterruptedException { latch2.release(); LOG.info("set keep alive and wait threads being removed"); - procExecutor.setKeepAliveTime(500L, TimeUnit.MILLISECONDS); int threads2 = waitThreadCount(2); LOG.info("threads got removed: " + (threads1 - threads2)); Assert.assertEquals(2, threads2); diff --git a/confignode/src/test/java/org/apache/iotdb/confignode/procedure/store/TestProcedureStore.java b/confignode/src/test/java/org/apache/iotdb/confignode/procedure/store/TestProcedureStore.java index e7a51885f156..6d691cc2017c 100644 --- a/confignode/src/test/java/org/apache/iotdb/confignode/procedure/store/TestProcedureStore.java +++ b/confignode/src/test/java/org/apache/iotdb/confignode/procedure/store/TestProcedureStore.java @@ -48,8 +48,7 @@ public class TestProcedureStore extends TestProcedureBase { @Override protected void initExecutor() { this.env = new TestProcEnv(); - this.procStore = - new org.apache.iotdb.confignode.procedure.store.ProcedureStore(TEST_DIR, factory); + this.procStore = new ProcedureStore(TEST_DIR, factory); this.procExecutor = new ProcedureExecutor<>(env, procStore); this.env.setScheduler(this.procExecutor.getScheduler()); this.procExecutor.init(WORK_THREAD); @@ -57,8 +56,7 @@ protected void initExecutor() { @Test public void testUpdate() { - org.apache.iotdb.confignode.procedure.store.ProcedureStore procedureStore = - new org.apache.iotdb.confignode.procedure.store.ProcedureStore(TEST_DIR, factory); + ProcedureStore procedureStore = new ProcedureStore(TEST_DIR, factory); IncProcedure incProcedure = new IncProcedure(); procedureStore.update(incProcedure); List procedureList = new ArrayList<>(); @@ -85,8 +83,7 @@ public void testChildProcedureLoad() { // stop service ProcedureTestUtil.stopService(procExecutor, procExecutor.getScheduler(), procStore); ConcurrentHashMap procedures = procExecutor.getProcedures(); - org.apache.iotdb.confignode.procedure.store.ProcedureStore procedureStore = - new ProcedureStore(TEST_DIR, new TestProcedureFactory()); + ProcedureStore procedureStore = new ProcedureStore(TEST_DIR, new TestProcedureFactory()); List procedureList = new ArrayList<>(); procedureStore.load(procedureList); Assert.assertEquals(childCount + 1, procedureList.size()); From 94578876e63f07e1e64d97e7f6aee7e83252b942 Mon Sep 17 00:00:00 2001 From: Pengyu Chen <48903014+trin1t@users.noreply.github.com> Date: Fri, 20 May 2022 18:12:29 +0800 Subject: [PATCH 064/436] [IOTDB-2305] Anomaly Detection for Library UDF (#5864) Co-authored-by: Jialin Qiao Co-authored-by: FrankHWD Co-authored-by: Haonan Co-authored-by: Steve Yurong Su --- library-udf/pom.xml | 22 +- .../apache/iotdb/library/anomaly/UDTFIQR.java | 111 ++ .../iotdb/library/anomaly/UDTFKSigma.java | 153 ++ .../apache/iotdb/library/anomaly/UDTFLOF.java | 215 +++ .../iotdb/library/anomaly/UDTFMissDetect.java | 74 + .../iotdb/library/anomaly/UDTFRange.java | 97 ++ .../library/anomaly/UDTFTwoSidedFilter.java | 92 + .../library/anomaly/util/MissDetector.java | 240 +++ .../anomaly/util/StreamMissDetector.java | 189 ++ .../library/anomaly/util/WindowDetect.java | 96 ++ .../org/apache/iotdb/library/util/Util.java | 3 - .../iotdb/library/anomaly/AnomalyTests.java | 1534 +++++++++++++++++ 12 files changed, 2820 insertions(+), 6 deletions(-) create mode 100644 library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFIQR.java create mode 100644 library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFKSigma.java create mode 100644 library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFLOF.java create mode 100644 library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFMissDetect.java create mode 100644 library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFRange.java create mode 100644 library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFTwoSidedFilter.java create mode 100644 library-udf/src/main/java/org/apache/iotdb/library/anomaly/util/MissDetector.java create mode 100644 library-udf/src/main/java/org/apache/iotdb/library/anomaly/util/StreamMissDetector.java create mode 100644 library-udf/src/main/java/org/apache/iotdb/library/anomaly/util/WindowDetect.java create mode 100644 library-udf/src/test/java/org/apache/iotdb/library/anomaly/AnomalyTests.java diff --git a/library-udf/pom.xml b/library-udf/pom.xml index bc0cdf6c0505..4192290ad9c8 100644 --- a/library-udf/pom.xml +++ b/library-udf/pom.xml @@ -41,13 +41,13 @@ org.apache.iotdb iotdb-server ${project.version} - provided + compile org.apache.iotdb tsfile ${project.version} - provided + compile org.apache.iotdb @@ -113,9 +113,25 @@ org.apache.iotdb iotdb-jdbc - 0.14.0-SNAPSHOT + ${project.version} test + + org.apache.iotdb + iotdb-server + ${project.version} + compile + + + org.apache.iotdb + node-commons + ${project.version} + compile + + + com.google.guava + guava + diff --git a/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFIQR.java b/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFIQR.java new file mode 100644 index 000000000000..5f358200ada6 --- /dev/null +++ b/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFIQR.java @@ -0,0 +1,111 @@ +/* + * 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.iotdb.library.anomaly; + +import org.apache.iotdb.db.query.udf.api.UDTF; +import org.apache.iotdb.db.query.udf.api.access.Row; +import org.apache.iotdb.db.query.udf.api.collector.PointCollector; +import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.library.util.Util; +import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; + +import com.google.common.math.Quantiles; + +import java.util.ArrayList; + +/* +This function is used to detect anomalies based on IQR. +Stream swap require user to provide Q1 and Q3, while global swap does not. +*/ +public class UDTFIQR implements UDTF { + ArrayList value = new ArrayList<>(); + ArrayList timestamp = new ArrayList<>(); + String compute = "batch"; + double q1 = 0.0d; + double q3 = 0.0d; + double iqr = 0.0d; + + public void validate(UDFParameterValidator validator) throws Exception { + validator + .validateInputSeriesNumber(1) + .validateInputSeriesDataType( + 0, TSDataType.INT32, TSDataType.INT64, TSDataType.FLOAT, TSDataType.DOUBLE) + .validate( + x -> ((String) x).equalsIgnoreCase("batch") || ((String) x).equalsIgnoreCase("stream"), + "Parameter \"compute\" is illegal. Please use \"batch\" (for default) or \"stream\".", + validator.getParameters().getStringOrDefault("compute", "batch")) + .validate( + params -> (double) params[0] < (double) params[1], + "parameter $q1$ should be smaller than $q3$", + validator.getParameters().getDoubleOrDefault("q1", -1), + validator.getParameters().getDoubleOrDefault("q3", 1)); + } + + @Override + public void beforeStart(UDFParameters parameters, UDTFConfigurations configurations) + throws Exception { + value.clear(); + timestamp.clear(); + q1 = 0.0d; + q3 = 0.0d; + iqr = 0.0d; + configurations + .setAccessStrategy(new RowByRowAccessStrategy()) + .setOutputDataType(TSDataType.DOUBLE); + compute = parameters.getStringOrDefault("compute", "batch"); + if (compute.equalsIgnoreCase("stream")) { + q1 = parameters.getDouble("q1"); + q3 = parameters.getDouble("q3"); + iqr = q3 - q1; + } + } + + @Override + public void transform(Row row, PointCollector collector) throws Exception { + if (compute.equalsIgnoreCase("stream") && q3 > q1) { + double v = Util.getValueAsDouble(row); + if (v < q1 - 1.5 * iqr || v > q3 + 1.5 * iqr) { + collector.putDouble(row.getTime(), v); + } + } else if (compute.equalsIgnoreCase("batch")) { + double v = Util.getValueAsDouble(row); + value.add(v); + timestamp.add(row.getTime()); + } + } + + @Override + public void terminate(PointCollector collector) throws Exception { + if (compute.equalsIgnoreCase("batch")) { + q1 = Quantiles.quartiles().index(1).compute(value); + q3 = Quantiles.quartiles().index(3).compute(value); + iqr = q3 - q1; + } + for (int i = 0; i < value.size(); i++) { + double v = value.get(i); + if (v < q1 - 1.5 * iqr || v > q3 + 1.5 * iqr) { + collector.putDouble(timestamp.get(i), v); + } + } + } +} diff --git a/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFKSigma.java b/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFKSigma.java new file mode 100644 index 000000000000..4c734115efbe --- /dev/null +++ b/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFKSigma.java @@ -0,0 +1,153 @@ +/* + * 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.iotdb.library.anomaly; + +import org.apache.iotdb.db.query.udf.api.UDTF; +import org.apache.iotdb.db.query.udf.api.access.Row; +import org.apache.iotdb.db.query.udf.api.collector.PointCollector; +import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.library.util.CircularQueue; +import org.apache.iotdb.library.util.LongCircularQueue; +import org.apache.iotdb.library.util.Util; +import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; + +/** This function detects outliers which lies over average +/- k * sigma. */ +public class UDTFKSigma implements UDTF { + private double mean = 0.0; + private double var = 0.0; + private double sumX2 = 0.0; + private double sumX1 = 0.0; + private double multipleK; + private int windowSize = 0; + private CircularQueue v; + private LongCircularQueue t; + private TSDataType dataType; + + @Override + public void validate(UDFParameterValidator validator) throws Exception { + validator + .validateInputSeriesNumber(1) + .validateInputSeriesDataType( + 0, TSDataType.INT32, TSDataType.INT64, TSDataType.FLOAT, TSDataType.DOUBLE) + .validate( + x -> (int) x > 0, + "Window size should be larger than 0.", + validator.getParameters().getIntOrDefault("window", 10)) + .validate( + x -> (double) x > 0, + "Parameter k should be larger than 0.", + validator.getParameters().getDoubleOrDefault("k", 3)); + } + + @Override + public void beforeStart(UDFParameters udfParameters, UDTFConfigurations udtfConfigurations) + throws Exception { + udtfConfigurations + .setAccessStrategy(new RowByRowAccessStrategy()) + .setOutputDataType(udfParameters.getDataType(0)); + this.multipleK = udfParameters.getDoubleOrDefault("k", 3); + this.dataType = udfParameters.getDataType(0); + this.windowSize = udfParameters.getIntOrDefault("window", 10000); + this.v = new CircularQueue<>(windowSize); + this.t = new LongCircularQueue(windowSize); + } + + @Override + public void transform(Row row, PointCollector collector) throws Exception { + double value = Util.getValueAsDouble(row); + long timestamp = row.getTime(); + if (Double.isFinite(value) && !Double.isNaN(value)) { + if (v.isFull()) { + double frontValue = Double.parseDouble(v.pop().toString()); + switch (dataType) { + case INT32: + v.push(row.getInt(0)); + break; + case INT64: + v.push(row.getLong(0)); + break; + case DOUBLE: + v.push(row.getDouble(0)); + break; + case FLOAT: + v.push(row.getFloat(0)); + break; + } + t.pop(); + t.push(timestamp); + this.sumX1 = this.sumX1 - frontValue + value; + this.sumX2 = this.sumX2 - frontValue * frontValue + value * value; + this.mean = this.sumX1 / v.getSize(); + this.var = this.sumX2 / v.getSize() - this.mean * this.mean; + if (Math.abs(value - mean) + > multipleK * Math.sqrt(this.var * v.getSize() / (v.getSize() - 1))) { + Util.putValue(collector, dataType, timestamp, Util.getValueAsObject(row)); + } + } else { + switch (dataType) { + case INT32: + v.push(row.getInt(0)); + break; + case INT64: + v.push(row.getLong(0)); + break; + case DOUBLE: + v.push(row.getDouble(0)); + break; + case FLOAT: + v.push(row.getFloat(0)); + break; + } + t.push(timestamp); + this.sumX1 = this.sumX1 + value; + this.sumX2 = this.sumX2 + value * value; + this.mean = this.sumX1 / v.getSize(); + this.var = this.sumX2 / v.getSize() - this.mean * this.mean; + if (v.getSize() == this.windowSize) { + double stddev = Math.sqrt(this.var * v.getSize() / (v.getSize() - 1)); + for (int i = 0; i < v.getSize(); i++) { + Object v = this.v.get(i); + timestamp = this.t.get(i); + if (Math.abs(Double.parseDouble(v.toString()) - mean) > multipleK * stddev) { + Util.putValue(collector, dataType, timestamp, v); + } + } + } + } + } + } + + @Override + public void terminate(PointCollector collector) throws Exception { + if (!v.isFull() && v.getSize() > 1) { + double stddev = Math.sqrt(this.var * v.getSize() / (v.getSize() - 1)); + for (int i = 0; i < v.getSize(); i++) { + Object v = this.v.get(i); + long timestamp = this.t.get(i); + if (Math.abs(Double.parseDouble(v.toString()) - mean) > multipleK * stddev) { + Util.putValue(collector, dataType, timestamp, v); + } + } + } + } +} diff --git a/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFLOF.java b/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFLOF.java new file mode 100644 index 000000000000..5c5d8d229475 --- /dev/null +++ b/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFLOF.java @@ -0,0 +1,215 @@ +/* + * 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.iotdb.library.anomaly; + +import org.apache.iotdb.db.query.udf.api.UDTF; +import org.apache.iotdb.db.query.udf.api.access.RowWindow; +import org.apache.iotdb.db.query.udf.api.collector.PointCollector; +import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; +import org.apache.iotdb.library.util.Util; +import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; + +/** This function is used to detect density anomaly of time series. */ +public class UDTFLOF implements UDTF { + private double threshold; + private int multipleK; + private int dim; + private String method = "default"; + private int window; + + int partition(Double[][] a, int left, int right) { + Double key = a[left][1]; + Double key2 = a[left][0]; + while (left < right) { + while (left < right && a[right][1] >= key) { + right--; + } + if (left < right) { + a[left][0] = a[right][0]; + a[left][1] = a[right][1]; + } + while (left < right && a[left][1] <= key) { + left++; + } + if (left < right) { + a[right][0] = a[left][0]; + a[right][1] = a[left][1]; + } + } + a[left][0] = key2; + a[left][1] = key; + return left; + } + + Double findKthNum(Double[][] a, int left, int right, int k) { + int index = partition(a, left, right); + if (index + 1 == k) { + return a[index][0]; + } else if (index + 1 < k) { + return findKthNum(a, index + 1, right, k); + } else { + return findKthNum(a, left, index - 1, k); + } + } + + public double getLOF(Double[][] knn, Double[] x, int length) { + double sum = 0; + for (int i = 0; i < length; i++) { + Double[] o = knn[i]; + sum += getLocDens(knn, o, length) / getLocDens(knn, x, length); + } + return sum / multipleK; + } + + public double getLocDens(Double[][] knn, Double[] x, int length) { + Double[] nnk = findKthPoint(knn, x, length); + + double sum = 0; + for (int i = 0; i < length; i++) { + Double[] o = knn[i]; + sum += reachDist(o, x, nnk); + } + return sum / multipleK; + } + + public Double[] findKthPoint(Double[][] knn, Double[] x, int length) { + int index; + double minDist = dist(knn[0], x); + Double[][] d = new Double[length][2]; + for (int i = 0; i < length; i++) { + d[i][0] = (double) i; + d[i][1] = dist(knn[i], x); + } + index = (int) (double) (findKthNum(d, 0, length - 1, multipleK + 1)); + return knn[index]; + } + + public double reachDist(Double[] o, Double[] x, Double[] nnk) { + return Math.max(dist(o, x), dist(nnk, x)); + } + + private double dist(Double[] nnk, Double[] x) { + double sum = 0; + for (int i = 0; i < nnk.length; i++) { + sum += (nnk[i] - x[i]) * (nnk[i] - x[i]); + } + return Math.sqrt(sum); + } + + @Override + public void validate(UDFParameterValidator validator) throws Exception { + validator.validateInputSeriesDataType( + 0, TSDataType.INT32, TSDataType.INT64, TSDataType.FLOAT, TSDataType.DOUBLE); + } + + @Override + public void beforeStart(UDFParameters parameters, UDTFConfigurations configurations) + throws Exception { + configurations + .setAccessStrategy( + new SlidingSizeWindowAccessStrategy(parameters.getIntOrDefault("window", 10000))) + .setOutputDataType(TSDataType.DOUBLE); + this.multipleK = parameters.getIntOrDefault("k", 3); + this.dim = parameters.getPaths().size(); + this.method = parameters.getStringOrDefault("method", "default"); + this.window = parameters.getIntOrDefault("window", 5); + } + + @Override + public void transform(RowWindow rowWindow, PointCollector collector) throws Exception { + if (this.method.equals("default")) { + int size = rowWindow.windowSize(); + Double[][] knn = new Double[size][dim]; + long[] timestamp = new long[size]; + int i = 0; + int row = 0; + while (row < rowWindow.windowSize()) { + timestamp[i] = rowWindow.getRow(row).getTime(); + for (int j = 0; j < dim; j++) { + if (!rowWindow.getRow(row).isNull(j)) { + knn[i][j] = Util.getValueAsDouble(rowWindow.getRow(i), j); + } else { + i--; + size--; + break; + } + } + i++; + row++; + } + if (size > multipleK) { + double[] lof = new double[size]; + for (int m = 0; m < size; m++) { + try { + lof[m] = getLOF(knn, knn[m], size); + collector.putDouble(timestamp[m], lof[m]); + } catch (Exception e) { + throw new Exception("Fail to get LOF " + m, e); + } + } + } + } else if (this.method.equals("series")) { + int size = rowWindow.windowSize() - window + 1; + if (size > 0) { + Double[][] knn = new Double[size][window]; + long[] timestamp = new long[rowWindow.windowSize()]; + double temp; + int i = 0; + int row = 0; + while (row < rowWindow.windowSize()) { + timestamp[i] = rowWindow.getRow(row).getTime(); + if (!rowWindow.getRow(row).isNull(0)) { + temp = Util.getValueAsDouble(rowWindow.getRow(row), 0); + for (int p = 0; p < window; p++) { + if (i - p < 0) { + break; + } + if (i - p < size) { + knn[i - p][p] = temp; + } + } + } else { + i--; + size--; + } + i++; + row++; + } + if (size > multipleK) { + double[] lof = new double[size]; + for (int m = 0; m < size; m++) { + try { + lof[m] = getLOF(knn, knn[m], size); + collector.putDouble(timestamp[m], lof[m]); + } catch (Exception e) { + throw new Exception("Fail to get LOF " + m, e); + } + } + } + } + } + } + + @Override + public void terminate(PointCollector collector) throws Exception {} +} diff --git a/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFMissDetect.java b/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFMissDetect.java new file mode 100644 index 000000000000..a146799b1944 --- /dev/null +++ b/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFMissDetect.java @@ -0,0 +1,74 @@ +/* + * 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.iotdb.library.anomaly; + +import org.apache.iotdb.db.query.udf.api.UDTF; +import org.apache.iotdb.db.query.udf.api.access.Row; +import org.apache.iotdb.db.query.udf.api.collector.PointCollector; +import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.library.anomaly.util.StreamMissDetector; +import org.apache.iotdb.library.util.Util; +import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; + +/** This function is used to detect missing anomalies. */ +public class UDTFMissDetect implements UDTF { + + private StreamMissDetector detector; + + @Override + public void validate(UDFParameterValidator validator) throws Exception { + validator + .validateInputSeriesNumber(1) + .validateInputSeriesDataType( + 0, TSDataType.DOUBLE, TSDataType.FLOAT, TSDataType.INT32, TSDataType.INT64) + .validate( + x -> (int) x >= 10, + "minlen should be an integer greater than or equal to 10.", + validator.getParameters().getIntOrDefault("minlen", 10)); + } + + @Override + public void beforeStart(UDFParameters udfp, UDTFConfigurations udtfc) throws Exception { + udtfc.setAccessStrategy(new RowByRowAccessStrategy()).setOutputDataType(TSDataType.BOOLEAN); + int minLength = udfp.getIntOrDefault("minlen", 10); + this.detector = new StreamMissDetector(minLength); + } + + @Override + public void transform(Row row, PointCollector collector) throws Exception { + detector.insert(row.getTime(), Util.getValueAsDouble(row)); + while (detector.hasNext()) { + collector.putBoolean(detector.getOutTime(), detector.getOutValue()); + detector.next(); + } + } + + @Override + public void terminate(PointCollector collector) throws Exception { + detector.flush(); + while (detector.hasNext()) { + collector.putBoolean(detector.getOutTime(), detector.getOutValue()); + detector.next(); + } + } +} diff --git a/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFRange.java b/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFRange.java new file mode 100644 index 000000000000..c0fec326962c --- /dev/null +++ b/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFRange.java @@ -0,0 +1,97 @@ +/* + * 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.iotdb.library.anomaly; + +import org.apache.iotdb.db.query.udf.api.UDTF; +import org.apache.iotdb.db.query.udf.api.access.Row; +import org.apache.iotdb.db.query.udf.api.collector.PointCollector; +import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.library.util.Util; +import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; + +/** This function is used to detect range anomaly of time series. */ +public class UDTFRange implements UDTF { + private TSDataType dataType; + private double upperBound; + private double lowerBound; + + @Override + public void validate(UDFParameterValidator validator) throws Exception { + validator + .validateInputSeriesNumber(1) + .validateInputSeriesDataType( + 0, TSDataType.INT32, TSDataType.INT64, TSDataType.FLOAT, TSDataType.DOUBLE); + } + + @Override + public void beforeStart(UDFParameters parameters, UDTFConfigurations configurations) + throws Exception { + configurations + .setAccessStrategy(new RowByRowAccessStrategy()) + .setOutputDataType(parameters.getDataType(0)); + this.lowerBound = parameters.getDouble("lower_bound"); + this.upperBound = parameters.getDouble("upper_bound"); + this.dataType = parameters.getDataType(0); + } + + @Override + public void transform(Row row, PointCollector collector) throws Exception { + int intValue; + long longValue; + float floatValue; + double doubleValue; + long timestamp; + timestamp = row.getTime(); + switch (dataType) { + case INT32: + intValue = row.getInt(0); + if (intValue > upperBound || intValue < lowerBound) { + Util.putValue(collector, dataType, timestamp, intValue); + } + break; + case INT64: + longValue = row.getLong(0); + if (longValue > upperBound || longValue < lowerBound) { + Util.putValue(collector, dataType, timestamp, longValue); + } + break; + case FLOAT: + floatValue = row.getFloat(0); + if (floatValue > upperBound || floatValue < lowerBound) { + Util.putValue(collector, dataType, timestamp, floatValue); + } + break; + case DOUBLE: + doubleValue = row.getDouble(0); + if (doubleValue > upperBound || doubleValue < lowerBound) { + Util.putValue(collector, dataType, timestamp, doubleValue); + } + break; + default: + throw new Exception("No such kind of data type."); + } + } + + @Override + public void terminate(PointCollector collector) throws Exception {} +} diff --git a/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFTwoSidedFilter.java b/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFTwoSidedFilter.java new file mode 100644 index 000000000000..80a4f545d5ac --- /dev/null +++ b/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFTwoSidedFilter.java @@ -0,0 +1,92 @@ +/* + * 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.iotdb.library.anomaly; + +import org.apache.iotdb.db.query.udf.api.UDTF; +import org.apache.iotdb.db.query.udf.api.access.RowWindow; +import org.apache.iotdb.db.query.udf.api.collector.PointCollector; +import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; +import org.apache.iotdb.library.anomaly.util.WindowDetect; +import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; + +/** + * The function is used to filter anomalies of a numeric time series based on two-sided window + * detection. + */ +public class UDTFTwoSidedFilter implements UDTF { + private double len; + private double threshold; + + @Override + public void validate(UDFParameterValidator validator) throws Exception { + validator + .validateInputSeriesNumber(1) + .validateInputSeriesDataType( + 0, TSDataType.INT32, TSDataType.INT64, TSDataType.FLOAT, TSDataType.DOUBLE); + } + + @Override + public void beforeStart(UDFParameters parameters, UDTFConfigurations configurations) + throws Exception { + configurations + .setAccessStrategy(new SlidingSizeWindowAccessStrategy(Integer.MAX_VALUE)) + .setOutputDataType(parameters.getDataType(0)); + this.len = parameters.getDoubleOrDefault("len", 5); + this.threshold = parameters.getDoubleOrDefault("threshold", 0.4); + TSDataType dataType = parameters.getDataType(0); + } + + @Override + public void transform(RowWindow rowWindow, PointCollector collector) throws Exception { + WindowDetect wd = new WindowDetect(rowWindow.getRowIterator(), len, threshold); + double[] repaired = wd.getRepaired(); + long[] time = wd.getTime(); + switch (rowWindow.getDataType(0)) { + case DOUBLE: + for (int i = 0; i < time.length; i++) { + collector.putDouble(time[i], repaired[i]); + } + break; + case FLOAT: + for (int i = 0; i < time.length; i++) { + collector.putFloat(time[i], (float) repaired[i]); + } + break; + case INT32: + for (int i = 0; i < time.length; i++) { + collector.putInt(time[i], (int) Math.round(repaired[i])); + } + break; + case INT64: + for (int i = 0; i < time.length; i++) { + collector.putLong(time[i], Math.round(repaired[i])); + } + break; + default: + throw new Exception("No such kind of data type."); + } + } + + @Override + public void terminate(PointCollector collector) throws Exception {} +} diff --git a/library-udf/src/main/java/org/apache/iotdb/library/anomaly/util/MissDetector.java b/library-udf/src/main/java/org/apache/iotdb/library/anomaly/util/MissDetector.java new file mode 100644 index 000000000000..18a2db5cf223 --- /dev/null +++ b/library-udf/src/main/java/org/apache/iotdb/library/anomaly/util/MissDetector.java @@ -0,0 +1,240 @@ +/* + * 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.iotdb.library.anomaly.util; + +import org.apache.iotdb.db.query.udf.api.access.Row; +import org.apache.iotdb.db.query.udf.api.access.RowIterator; +import org.apache.iotdb.library.util.Util; + +import org.apache.commons.math3.stat.regression.SimpleRegression; +import org.eclipse.collections.impl.list.mutable.primitive.DoubleArrayList; +import org.eclipse.collections.impl.list.mutable.primitive.IntArrayList; +import org.eclipse.collections.impl.list.mutable.primitive.LongArrayList; + +import java.util.ArrayList; + +/** Anomaly detection for anomalies of data missing. */ +public class MissDetector { + + private final LongArrayList time = new LongArrayList(); + private final DoubleArrayList value = new DoubleArrayList(); + private final IntArrayList predictLabel = new IntArrayList(); + private final int len; + + private int minLength; + private double threshold = 0.9999; + private double lineBoundary = 0.6; + private long startTime; + private int windowCnt = 0; + private double r2 = 0; + private final ArrayList anomalyList = new ArrayList<>(); + + public MissDetector(RowIterator iterator, int minLength) throws Exception { + while (iterator.hasNextRow()) { + Row row = iterator.next(); + double v = Util.getValueAsDouble(row); + if (Double.isFinite(v)) { + this.time.add(row.getTime()); + this.value.add(v); + } + } + this.len = this.time.size(); + this.minLength = minLength; + } + + public void detect() { + getPredictLabel().addAll(new int[getLen()]); + this.startTime = getTime().get(0); + int i = 0; + int windowSize = minLength / 2; + double[][] data = new double[windowSize][2]; + SimpleRegression regression = new SimpleRegression(); + while (i + windowSize < getLen()) { + for (int j = 0; j < windowSize; j++) { + data[j][0] = getTime().get(i + j) - startTime; + data[j][1] = getValue().get(i + j); + } + regression.addData(data); + double alpha = regression.getRSquare(); + if (Double.isNaN(alpha) || alpha >= threshold) { + i = extend(regression, i, i + windowSize); + } else { + i += windowSize; + } + regression.clear(); + r2 += Double.isNaN(alpha) ? 1 : alpha; + windowCnt++; + } + label(); + } + + private int extend(SimpleRegression regression, int start, int end) { + boolean horizon = Double.isNaN(regression.getRSquare()); + double standard = regression.getIntercept(); + int bindex = start; + while (bindex > 0) { + bindex--; + regression.addData(getTime().get(bindex) - startTime, getValue().get(bindex)); + double alpha = regression.getRSquare(); + if ((horizon && getValue().get(bindex) != standard) || (!horizon && alpha < threshold)) { + break; + } + } + regression.removeData(getTime().get(bindex) - startTime, getValue().get(bindex)); + if (bindex == 0) { + return end; + } + int findex = end; + while (findex < getLen()) { + regression.addData(getTime().get(findex) - startTime, getValue().get(findex)); + double alpha = regression.getRSquare(); + if ((horizon && getValue().get(findex) != standard) || (!horizon && alpha < threshold)) { + break; + } + findex++; + } + if (findex == getLen()) { + return end; + } + MissingSubSeries m = new MissingSubSeries(bindex + 1, findex); + anomalyList.add(m); + return findex; + } + + private void label() { + if (r2 / windowCnt < lineBoundary) { + for (MissingSubSeries m : anomalyList) { + if (m.getLength() >= minLength) { + for (int i = m.getStart(); i < m.getEnd(); i++) { + getPredictLabel().set(i, 1); + } + } + } + } + } + + /** @return the minLength */ + public int getMinLength() { + return minLength; + } + + /** @param minLength the minLength to set */ + public void setMinLength(int minLength) { + this.minLength = minLength; + } + + /** @return the threshold */ + public double getThreshold() { + return threshold; + } + + /** @param threshold the threshold to set */ + public void setThreshold(double threshold) { + this.threshold = threshold; + } + + private class MissingSubSeries implements Comparable { + + private int start; + private int end; + private double slope; + + public MissingSubSeries() {} + + public MissingSubSeries(int start, int end) { + this.start = start; + this.end = end; + this.slope = + (getValue().get(end - 1) - getValue().get(start)) + / (getTime().get(end - 1) - getTime().get(start)); + } + + @Override + public String toString() { + return String.format( + "Start: %d, End: %d, Length: %d, Slope: %.12f", start, end, end - start, slope); + } + + public int getLength() { + return end - start; + } + + /** @return the start */ + public int getStart() { + return start; + } + + /** @param start the start to set */ + public void setStart(int start) { + this.start = start; + } + + /** @return the end */ + public int getEnd() { + return end; + } + + /** @param end the end to set */ + public void setEnd(int end) { + this.end = end; + } + + /** @return the slope */ + public double getSlope() { + return slope; + } + + /** @param slope the slope to set */ + public void setSlope(double slope) { + this.slope = slope; + } + + @Override + public int compareTo(MissingSubSeries o) { + if (this.getLength() > o.getLength()) { + return -1; + } else if (this.getLength() == o.getLength()) { + return 0; + } else { + return 1; + } + } + } + + /** @return the predictLabel */ + public IntArrayList getPredictLabel() { + return predictLabel; + } + + /** @return the len */ + public int getLen() { + return len; + } + + /** @return the time */ + public LongArrayList getTime() { + return time; + } + + /** @return the value */ + public DoubleArrayList getValue() { + return value; + } +} diff --git a/library-udf/src/main/java/org/apache/iotdb/library/anomaly/util/StreamMissDetector.java b/library-udf/src/main/java/org/apache/iotdb/library/anomaly/util/StreamMissDetector.java new file mode 100644 index 000000000000..fe902e6c13f5 --- /dev/null +++ b/library-udf/src/main/java/org/apache/iotdb/library/anomaly/util/StreamMissDetector.java @@ -0,0 +1,189 @@ +/* + * 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.iotdb.library.anomaly.util; + +import org.apache.iotdb.library.util.BooleanCircularQueue; +import org.apache.iotdb.library.util.DoubleCircularQueue; +import org.apache.iotdb.library.util.LongCircularQueue; + +import org.apache.commons.math3.stat.regression.SimpleRegression; + +/** Streaming anomaly detection for anomalies of data missing. */ +public class StreamMissDetector { + + private final LongCircularQueue timeWindow = new LongCircularQueue(); + private final DoubleCircularQueue valueWindow = new DoubleCircularQueue(); + private final LongCircularQueue timeBuffer = new LongCircularQueue(); + private final BooleanCircularQueue labelBuffer = new BooleanCircularQueue(); + private final SimpleRegression regression = new SimpleRegression(); + private final double threshold = 0.9999; + private int minLength; + private int state; + private long startTime; + private int missingStartIndex; + private boolean horizon; + private double standard; + + public StreamMissDetector(int minLength) { + this.state = 0; + this.startTime = -1; + this.minLength = minLength; + } + + public void insert(long time, double value) { + timeWindow.push(time); + valueWindow.push(value); + if (startTime < 0) { + startTime = time; + } + switch (state) { + case 0: + if (timeWindow.getSize() >= getWindowSize()) { + linearRegress(0, getWindowSize()); + double alpha = regression.getRSquare(); + if (Double.isNaN(alpha) || alpha > threshold) { + missingStartIndex = 0; + state = 2; + } else { + regression.clear(); + state = 1; + } + } + break; + case 1: + if (timeWindow.getSize() >= getWindowSize() * 2) { + linearRegress(getWindowSize(), getWindowSize() * 2); + double alpha = regression.getRSquare(); + if (Double.isNaN(alpha) || alpha > threshold) { + missingStartIndex = backExtend(); + state = 2; + } else { + for (int i = 0; i < getWindowSize(); i++) { + timeBuffer.push(timeWindow.pop()); + labelBuffer.push(false); + valueWindow.pop(); + } + regression.clear(); + state = 1; + } + } + break; + case 2: + regression.addData(time - startTime, value); + double alpha = regression.getRSquare(); + if ((horizon && value != standard) || (!horizon && alpha < threshold)) { + int missingEndIndex = timeWindow.getSize() - 1; + for (int i = 0; i < missingStartIndex; i++) { + timeBuffer.push(timeWindow.pop()); + labelBuffer.push(false); + valueWindow.pop(); + } + boolean label = missingEndIndex - missingStartIndex > minLength; + for (int i = missingStartIndex; i < missingEndIndex; i++) { + timeBuffer.push(timeWindow.pop()); + labelBuffer.push(label); + valueWindow.pop(); + } + regression.clear(); + state = 0; + } + } + } + + public void flush() { + switch (state) { + case 0: + case 1: + while (!timeWindow.isEmpty()) { + timeBuffer.push(timeWindow.pop()); + labelBuffer.push(false); + valueWindow.pop(); + } + break; + case 2: + boolean label = timeWindow.getSize() - missingStartIndex > minLength; + for (int i = 0; i < missingStartIndex; i++) { + timeBuffer.push(timeWindow.pop()); + labelBuffer.push(false); + valueWindow.pop(); + } + while (!timeWindow.isEmpty()) { + timeBuffer.push(timeWindow.pop()); + labelBuffer.push(label); + valueWindow.pop(); + } + } + } + + private int backExtend() { + horizon = Double.isNaN(regression.getRSquare()); + standard = regression.getIntercept(); + int bindex = getWindowSize(); + while (bindex > 0) { + bindex--; + regression.addData(timeWindow.get(bindex) - startTime, valueWindow.get(bindex)); + double alpha = regression.getRSquare(); + if ((horizon && valueWindow.get(bindex) != standard) || (!horizon && alpha < threshold)) { + break; + } + } + regression.removeData(timeWindow.get(bindex) - startTime, valueWindow.get(bindex)); + return bindex + 1; + } + + private void linearRegress(int start, int end) { + double[][] data = new double[getWindowSize()][2]; + for (int i = start; i < end; i++) { + data[i - start][0] = timeWindow.get(i) - startTime; + data[i - start][1] = valueWindow.get(i); + } + regression.addData(data); + } + + public boolean hasNext() { + return !timeBuffer.isEmpty(); + } + + public long getOutTime() { + return timeBuffer.getHead(); + } + + public boolean getOutValue() { + return labelBuffer.getHead(); + } + + public void next() { + timeBuffer.pop(); + labelBuffer.pop(); + } + + private int getWindowSize() { + return minLength / 2; + } + + public int getMinLength() { + return minLength; + } + + /** @param minLength the minLength to set */ + public void setMinLength(int minLength) { + this.minLength = minLength; + } +} diff --git a/library-udf/src/main/java/org/apache/iotdb/library/anomaly/util/WindowDetect.java b/library-udf/src/main/java/org/apache/iotdb/library/anomaly/util/WindowDetect.java new file mode 100644 index 000000000000..0d8e15d23ecd --- /dev/null +++ b/library-udf/src/main/java/org/apache/iotdb/library/anomaly/util/WindowDetect.java @@ -0,0 +1,96 @@ +/* + * 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.iotdb.library.anomaly.util; + +import org.apache.iotdb.db.query.udf.api.access.Row; +import org.apache.iotdb.db.query.udf.api.access.RowIterator; +import org.apache.iotdb.library.util.Util; + +import java.util.ArrayList; + +public class WindowDetect { + + protected int timeLength; + protected double len; + protected double threshold; + protected long[] time; + protected double[] original; + protected double[] repaired; + + public WindowDetect(RowIterator dataIterator, double l, double t) throws Exception { + len = l; + threshold = t; + ArrayList timeList = new ArrayList<>(); + ArrayList originList = new ArrayList<>(); + while (dataIterator.hasNextRow()) { + Row row = dataIterator.next(); + Double v = Util.getValueAsDouble(row); + timeList.add(row.getTime()); + if (v == null || !Double.isFinite(v)) { + originList.add(Double.NaN); + } else { + originList.add(v); + } + } + time = Util.toLongArray(timeList); + original = Util.toDoubleArray(originList); + timeLength = time.length; + repair(); + } + + private void repair() throws Exception { + ArrayList repairedList = new ArrayList<>(); + ArrayList timeList = new ArrayList<>(); + for (int i = 0; i < timeLength; i++) { + long curTime = time[i]; + double curValue = original[i]; + double valueBefore = 0.0; + double valueAfter = 0.0; + for (int j = (int) Math.max(0, i - len); j <= Math.min(timeLength - 1, i + len); j++) { + if (j < i && Math.abs(time[j] - curTime) <= len * 1000) { + valueBefore += original[j]; + } + if (j > i && Math.abs(time[j] - curTime) <= len * 1000) { + valueAfter += original[j]; + } + } + valueBefore = valueBefore / len; + valueAfter = valueAfter / len; + double p1 = Math.abs(valueBefore - curValue) / (Math.max(valueBefore, curValue) + 1); + double p2 = Math.abs(valueAfter - curValue) / (Math.max(valueAfter, curValue) + 1); + if (p1 < threshold || p2 < threshold) { + repairedList.add(curValue); + timeList.add(curTime); + } + } + time = Util.toLongArray(timeList); + repaired = Util.toDoubleArray(repairedList); + } + + /** @return the time */ + public long[] getTime() { + return time; + } + + /** @return the repaired */ + public double[] getRepaired() { + return repaired; + } +} diff --git a/library-udf/src/main/java/org/apache/iotdb/library/util/Util.java b/library-udf/src/main/java/org/apache/iotdb/library/util/Util.java index 0691bd3e1933..f1a1896546d4 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/util/Util.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/util/Util.java @@ -130,9 +130,6 @@ public static void putValue(PointCollector pc, TSDataType type, long t, Object o case DOUBLE: pc.putDouble(t, (Double) o); break; - case TEXT: - pc.putString(t, (String) o); - break; case BOOLEAN: pc.putBoolean(t, (Boolean) o); } diff --git a/library-udf/src/test/java/org/apache/iotdb/library/anomaly/AnomalyTests.java b/library-udf/src/test/java/org/apache/iotdb/library/anomaly/AnomalyTests.java new file mode 100644 index 000000000000..722ca6f67a87 --- /dev/null +++ b/library-udf/src/test/java/org/apache/iotdb/library/anomaly/AnomalyTests.java @@ -0,0 +1,1534 @@ +/* + * 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.iotdb.library.anomaly; + +import org.apache.iotdb.commons.exception.IllegalPathException; +import org.apache.iotdb.commons.exception.MetadataException; +import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.db.conf.IoTDBDescriptor; +import org.apache.iotdb.db.service.IoTDB; +import org.apache.iotdb.integration.env.ConfigFactory; +import org.apache.iotdb.integration.env.EnvFactory; +import org.apache.iotdb.tsfile.file.metadata.enums.CompressionType; +import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; +import org.apache.iotdb.tsfile.file.metadata.enums.TSEncoding; + +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import static org.junit.Assert.fail; + +public class AnomalyTests { + private static final float oldUdfCollectorMemoryBudgetInMB = + IoTDBDescriptor.getInstance().getConfig().getUdfCollectorMemoryBudgetInMB(); + private static final float oldUdfTransformerMemoryBudgetInMB = + IoTDBDescriptor.getInstance().getConfig().getUdfTransformerMemoryBudgetInMB(); + private static final float oldUdfReaderMemoryBudgetInMB = + IoTDBDescriptor.getInstance().getConfig().getUdfReaderMemoryBudgetInMB(); + + @BeforeClass + public static void setUp() throws Exception { + ConfigFactory.getConfig() + .setUdfCollectorMemoryBudgetInMB(5) + .setUdfTransformerMemoryBudgetInMB(5) + .setUdfReaderMemoryBudgetInMB(5); + EnvFactory.getEnv().initBeforeClass(); + createTimeSeries(); + generateData(); + registerUDF(); + } + + private static void createTimeSeries() throws MetadataException, IllegalPathException { + IoTDB.schemaProcessor.setStorageGroup(new PartialPath("root.vehicle")); + IoTDB.schemaProcessor.createTimeseries( + new PartialPath("root.vehicle.d1.s1"), + TSDataType.INT32, + TSEncoding.PLAIN, + CompressionType.UNCOMPRESSED, + null); + IoTDB.schemaProcessor.createTimeseries( + new PartialPath("root.vehicle.d1.s2"), + TSDataType.INT64, + TSEncoding.PLAIN, + CompressionType.UNCOMPRESSED, + null); + IoTDB.schemaProcessor.createTimeseries( + new PartialPath("root.vehicle.d1.s3"), + TSDataType.FLOAT, + TSEncoding.PLAIN, + CompressionType.UNCOMPRESSED, + null); + IoTDB.schemaProcessor.createTimeseries( + new PartialPath("root.vehicle.d1.s4"), + TSDataType.DOUBLE, + TSEncoding.PLAIN, + CompressionType.UNCOMPRESSED, + null); + IoTDB.schemaProcessor.createTimeseries( + new PartialPath("root.vehicle.d2.s1"), + TSDataType.INT32, + TSEncoding.PLAIN, + CompressionType.UNCOMPRESSED, + null); + IoTDB.schemaProcessor.createTimeseries( + new PartialPath("root.vehicle.d2.s2"), + TSDataType.INT64, + TSEncoding.PLAIN, + CompressionType.UNCOMPRESSED, + null); + IoTDB.schemaProcessor.createTimeseries( + new PartialPath("root.vehicle.d2.s3"), + TSDataType.FLOAT, + TSEncoding.PLAIN, + CompressionType.UNCOMPRESSED, + null); + IoTDB.schemaProcessor.createTimeseries( + new PartialPath("root.vehicle.d2.s4"), + TSDataType.DOUBLE, + TSEncoding.PLAIN, + CompressionType.UNCOMPRESSED, + null); + IoTDB.schemaProcessor.createTimeseries( + new PartialPath("root.vehicle.d3.s1"), + TSDataType.INT32, + TSEncoding.PLAIN, + CompressionType.UNCOMPRESSED, + null); + IoTDB.schemaProcessor.createTimeseries( + new PartialPath("root.vehicle.d3.s2"), + TSDataType.INT64, + TSEncoding.PLAIN, + CompressionType.UNCOMPRESSED, + null); + IoTDB.schemaProcessor.createTimeseries( + new PartialPath("root.vehicle.d3.s3"), + TSDataType.FLOAT, + TSEncoding.PLAIN, + CompressionType.UNCOMPRESSED, + null); + IoTDB.schemaProcessor.createTimeseries( + new PartialPath("root.vehicle.d3.s4"), + TSDataType.DOUBLE, + TSEncoding.PLAIN, + CompressionType.UNCOMPRESSED, + null); + IoTDB.schemaProcessor.createTimeseries( + new PartialPath("root.vehicle.d3.s5"), + TSDataType.INT32, + TSEncoding.PLAIN, + CompressionType.UNCOMPRESSED, + null); + IoTDB.schemaProcessor.createTimeseries( + new PartialPath("root.vehicle.d3.s6"), + TSDataType.INT64, + TSEncoding.PLAIN, + CompressionType.UNCOMPRESSED, + null); + IoTDB.schemaProcessor.createTimeseries( + new PartialPath("root.vehicle.d3.s7"), + TSDataType.FLOAT, + TSEncoding.PLAIN, + CompressionType.UNCOMPRESSED, + null); + IoTDB.schemaProcessor.createTimeseries( + new PartialPath("root.vehicle.d3.s8"), + TSDataType.DOUBLE, + TSEncoding.PLAIN, + CompressionType.UNCOMPRESSED, + null); + IoTDB.schemaProcessor.createTimeseries( + new PartialPath("root.vehicle.d4.s1"), + TSDataType.INT32, + TSEncoding.PLAIN, + CompressionType.UNCOMPRESSED, + null); + IoTDB.schemaProcessor.createTimeseries( + new PartialPath("root.vehicle.d4.s2"), + TSDataType.INT64, + TSEncoding.PLAIN, + CompressionType.UNCOMPRESSED, + null); + IoTDB.schemaProcessor.createTimeseries( + new PartialPath("root.vehicle.d4.s3"), + TSDataType.FLOAT, + TSEncoding.PLAIN, + CompressionType.UNCOMPRESSED, + null); + IoTDB.schemaProcessor.createTimeseries( + new PartialPath("root.vehicle.d4.s4"), + TSDataType.DOUBLE, + TSEncoding.PLAIN, + CompressionType.UNCOMPRESSED, + null); + IoTDB.schemaProcessor.createTimeseries( + new PartialPath("root.vehicle.d6.s1"), + TSDataType.INT32, + TSEncoding.PLAIN, + CompressionType.UNCOMPRESSED, + null); + IoTDB.schemaProcessor.createTimeseries( + new PartialPath("root.vehicle.d6.s2"), + TSDataType.INT64, + TSEncoding.PLAIN, + CompressionType.UNCOMPRESSED, + null); + IoTDB.schemaProcessor.createTimeseries( + new PartialPath("root.vehicle.d6.s3"), + TSDataType.FLOAT, + TSEncoding.PLAIN, + CompressionType.UNCOMPRESSED, + null); + IoTDB.schemaProcessor.createTimeseries( + new PartialPath("root.vehicle.d6.s4"), + TSDataType.DOUBLE, + TSEncoding.PLAIN, + CompressionType.UNCOMPRESSED, + null); + } + + private static void generateData() { + try (Connection connection = EnvFactory.getEnv().getConnection(); + Statement statement = connection.createStatement()) { + statement.addBatch( + String.format( + "insert into root.vehicle.d1(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 100, 0, 0, 0, 0)); + statement.addBatch( + String.format( + "insert into root.vehicle.d1(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 200, 2, 2, 2, 2)); + statement.addBatch( + String.format( + "insert into root.vehicle.d1(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 300, -2, -2, -2, -2)); + statement.addBatch( + String.format( + "insert into root.vehicle.d1(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 400, -1, -1, -1, -1)); + statement.addBatch( + String.format( + "insert into root.vehicle.d1(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 500, 10, 10, 10, 10)); + statement.addBatch( + String.format( + "insert into root.vehicle.d1(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 600, 1, 1, 1, 1)); + statement.addBatch( + String.format( + "insert into root.vehicle.d1(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 700, 0, 0, 0, 0)); + statement.addBatch( + String.format( + "insert into root.vehicle.d1(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 800, 2, 2, 2, 2)); + statement.addBatch( + String.format( + "insert into root.vehicle.d1(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 900, -1, -1, -1, -1)); + statement.addBatch( + String.format( + "insert into root.vehicle.d1(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 1000, 1, 1, 1, 1)); + statement.addBatch( + String.format( + "insert into root.vehicle.d2(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 100, 0, 0, 0, 0)); + statement.addBatch( + String.format( + "insert into root.vehicle.d2(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 200, 50, 50, 50, 50)); + statement.addBatch( + String.format( + "insert into root.vehicle.d2(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 300, 100, 100, 100, 100)); + statement.addBatch( + String.format( + "insert into root.vehicle.d2(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 400, 150, 150, 150, 150)); + statement.addBatch( + String.format( + "insert into root.vehicle.d2(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 500, 200, 200, 200, 200)); + statement.addBatch( + String.format( + "insert into root.vehicle.d2(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 600, 200, 200, 200, 200)); + statement.addBatch( + String.format( + "insert into root.vehicle.d2(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 700, 200, 200, 200, 200)); + statement.addBatch( + String.format( + "insert into root.vehicle.d2(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 800, 200, 200, 200, 200)); + statement.addBatch( + String.format( + "insert into root.vehicle.d2(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 900, 200, 200, 200, 200)); + statement.addBatch( + String.format( + "insert into root.vehicle.d2(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 1000, 200, 200, 200, 200)); + statement.addBatch( + String.format( + "insert into root.vehicle.d2(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 1100, 150, 150, 150, 150)); + statement.addBatch( + String.format( + "insert into root.vehicle.d2(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 1200, 100, 100, 100, 100)); + statement.addBatch( + String.format( + "insert into root.vehicle.d2(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 1300, 50, 50, 50, 50)); + statement.addBatch( + String.format( + "insert into root.vehicle.d2(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 1400, 0, 0, 0, 0)); + statement.addBatch( + String.format( + "insert into root.vehicle.d3(timestamp,s1,s2,s3,s4,s5,s6,s7,s8) values(%d,%d,%d,%d,%d,%d,%d,%d,%d))", + 100, 0, 0, 0, 0, 0, 0, 0, 0)); + statement.addBatch( + String.format( + "insert into root.vehicle.d3(timestamp,s1,s2,s3,s4,s5,s6,s7,s8) values(%d,%d,%d,%d,%d,%d,%d,%d,%d))", + 200, 0, 0, 0, 0, 1, 1, 1, 1)); + statement.addBatch( + String.format( + "insert into root.vehicle.d3(timestamp,s1,s2,s3,s4,s5,s6,s7,s8) values(%d,%d,%d,%d,%d,%d,%d,%d,%d))", + 300, 1, 1, 1, 1, 1, 1, 1, 1)); + statement.addBatch( + String.format( + "insert into root.vehicle.d3(timestamp,s1,s2,s3,s4,s5,s6,s7,s8) values(%d,%d,%d,%d,%d,%d,%d,%d,%d))", + 400, 1, 1, 1, 1, 0, 0, 0, 0)); + statement.addBatch( + String.format( + "insert into root.vehicle.d3(timestamp,s1,s2,s3,s4,s5,s6,s7,s8) values(%d,%d,%d,%d,%d,%d,%d,%d,%d))", + 500, 0, 0, 0, 0, -1, -1, -1, -1)); + statement.addBatch( + String.format( + "insert into root.vehicle.d3(timestamp,s1,s2,s3,s4,s5,s6,s7,s8) values(%d,%d,%d,%d,%d,%d,%d,%d,%d))", + 600, -1, -1, -1, -1, -1, -1, -1, -1)); + statement.addBatch( + String.format( + "insert into root.vehicle.d3(timestamp,s1,s2,s3,s4,s5,s6,s7,s8) values(%d,%d,%d,%d,%d,%d,%d,%d,%d))", + 700, -1, -1, -1, -1, 0, 0, 0, 0)); + statement.addBatch( + String.format( + "insert into root.vehicle.d3(timestamp,s1,s2,s3,s4,s5,s6,s7,s8) values(%d,%d,%d,%d,%d,%d,%d,%d,%d))", + 800, 2, 2, 2, 2, 2, 2, 2, 2)); + statement.addBatch( + String.format( + "insert into root.vehicle.d4(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 100, 0, 0, 0, 0)); + statement.addBatch( + String.format( + "insert into root.vehicle.d4(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 200, 1, 1, 1, 1)); + statement.addBatch( + String.format( + "insert into root.vehicle.d4(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 300, 0, 0, 0, 0)); + statement.addBatch( + String.format( + "insert into root.vehicle.d4(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 400, 1, 1, 1, 1)); + statement.addBatch( + String.format( + "insert into root.vehicle.d4(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 500, 0, 0, 0, 0)); + statement.addBatch( + String.format( + "insert into root.vehicle.d4(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 600, 0, 0, 0, 0)); + statement.addBatch( + String.format( + "insert into root.vehicle.d4(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 700, 0, 0, 0, 0)); + statement.addBatch( + String.format( + "insert into root.vehicle.d4(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 800, 0, 0, 0, 0)); + statement.addBatch( + String.format( + "insert into root.vehicle.d4(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 900, 0, 0, 0, 0)); + statement.addBatch( + String.format( + "insert into root.vehicle.d4(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 1000, 0, 0, 0, 0)); + statement.addBatch( + String.format( + "insert into root.vehicle.d4(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 1100, 0, 0, 0, 0)); + statement.addBatch( + String.format( + "insert into root.vehicle.d4(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 1200, 0, 0, 0, 0)); + statement.addBatch( + String.format( + "insert into root.vehicle.d4(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 1300, 0, 0, 0, 0)); + statement.addBatch( + String.format( + "insert into root.vehicle.d4(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 1400, 0, 0, 0, 0)); + statement.addBatch( + String.format( + "insert into root.vehicle.d4(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 1500, 0, 0, 0, 0)); + statement.addBatch( + String.format( + "insert into root.vehicle.d4(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 1600, 1, 1, 1, 1)); + statement.addBatch( + String.format( + "insert into root.vehicle.d4(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 1700, 0, 0, 0, 0)); + statement.addBatch( + String.format( + "insert into root.vehicle.d4(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 1800, 1, 1, 1, 1)); + statement.addBatch( + String.format( + "insert into root.vehicle.d4(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 1900, 0, 0, 0, 0)); + statement.addBatch( + String.format( + "insert into root.vehicle.d4(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 2000, 1, 1, 1, 1)); + statement.addBatch( + String.format( + "insert into root.vehicle.d6(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 100, 2002, 2002, 2002, 2002)); + statement.addBatch( + String.format( + "insert into root.vehicle.d6(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 200, 1946, 1946, 1946, 1946)); + statement.addBatch( + String.format( + "insert into root.vehicle.d6(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 300, 1958, 1958, 1958, 1958)); + statement.addBatch( + String.format( + "insert into root.vehicle.d6(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 400, 2012, 2012, 2012, 2012)); + statement.addBatch( + String.format( + "insert into root.vehicle.d6(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 500, 2051, 2051, 2051, 2051)); + statement.addBatch( + String.format( + "insert into root.vehicle.d6(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 600, 1898, 1898, 1898, 1898)); + statement.addBatch( + String.format( + "insert into root.vehicle.d6(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 700, 2014, 2014, 2014, 2014)); + statement.addBatch( + String.format( + "insert into root.vehicle.d6(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 800, 2052, 2052, 2052, 2052)); + statement.addBatch( + String.format( + "insert into root.vehicle.d6(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 900, 1935, 1935, 1935, 1935)); + statement.addBatch( + String.format( + "insert into root.vehicle.d6(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 1000, 1901, 1901, 1901, 1901)); + statement.addBatch( + String.format( + "insert into root.vehicle.d6(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 1100, 1972, 1972, 1972, 1972)); + statement.addBatch( + String.format( + "insert into root.vehicle.d6(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 1200, 1969, 1969, 1969, 1969)); + statement.addBatch( + String.format( + "insert into root.vehicle.d6(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 1300, 1984, 1984, 1984, 1984)); + statement.addBatch( + String.format( + "insert into root.vehicle.d6(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 1400, 2018, 2018, 2018, 2018)); + statement.addBatch( + String.format( + "insert into root.vehicle.d6(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 1500, 1484, 1484, 1484, 1484)); + statement.addBatch( + String.format( + "insert into root.vehicle.d6(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 1600, 1055, 1055, 1055, 1055)); + statement.addBatch( + String.format( + "insert into root.vehicle.d6(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 1700, 1050, 1050, 1050, 1050)); + statement.addBatch( + String.format( + "insert into root.vehicle.d6(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 1800, 1023, 1023, 1023, 1023)); + statement.addBatch( + String.format( + "insert into root.vehicle.d6(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 1900, 1056, 1056, 1056, 1056)); + statement.addBatch( + String.format( + "insert into root.vehicle.d6(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 2000, 978, 978, 978, 978)); + statement.addBatch( + String.format( + "insert into root.vehicle.d6(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 2100, 1050, 1050, 1050, 1050)); + statement.addBatch( + String.format( + "insert into root.vehicle.d6(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 2200, 1123, 1123, 1123, 1123)); + statement.addBatch( + String.format( + "insert into root.vehicle.d6(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 2300, 1150, 1150, 1150, 1150)); + statement.addBatch( + String.format( + "insert into root.vehicle.d6(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 2400, 1034, 1034, 1034, 1034)); + statement.addBatch( + String.format( + "insert into root.vehicle.d6(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 2500, 950, 950, 950, 950)); + statement.addBatch( + String.format( + "insert into root.vehicle.d6(timestamp,s1,s2,s3,s4) values(%d,%d,%d,%d,%d)", + 2600, 1059, 1059, 1059, 1059)); + statement.executeBatch(); + } catch (SQLException throwable) { + fail(throwable.getMessage()); + } + } + + private static void registerUDF() { + try (Connection connection = EnvFactory.getEnv().getConnection(); + Statement statement = connection.createStatement()) { + statement.execute("create function iqr as 'org.apache.iotdb.library.anomaly.UDTFIQR'"); + statement.execute("create function ksigma as 'org.apache.iotdb.library.anomaly.UDTFKSigma'"); + statement.execute( + "create function missdetect as 'org.apache.iotdb.library.anomaly.UDTFMissDetect'"); + statement.execute("create function lof as 'org.apache.iotdb.library.anomaly.UDTFLOF'"); + statement.execute("create function range as 'org.apache.iotdb.library.anomaly.UDTFRange'"); + statement.execute( + "create function TwoSidedFilter as 'org.apache.iotdb.library.anomaly.UDTFTwoSidedFilter'"); + } catch (SQLException throwable) { + fail(throwable.getMessage()); + } + } + + @AfterClass + public static void tearDown() throws Exception { + EnvFactory.getEnv().cleanAfterClass(); + ConfigFactory.getConfig() + .setUdfCollectorMemoryBudgetInMB(oldUdfCollectorMemoryBudgetInMB) + .setUdfTransformerMemoryBudgetInMB(oldUdfTransformerMemoryBudgetInMB) + .setUdfReaderMemoryBudgetInMB(oldUdfReaderMemoryBudgetInMB); + } + + @Test + public void testIRQR1() { + String sqlStr = "select iqr(d1.s1) from root.vehicle"; + try (Connection connection = EnvFactory.getEnv().getConnection(); + Statement statement = connection.createStatement()) { + ResultSet resultSet = statement.executeQuery(sqlStr); + resultSet.next(); + double result1 = resultSet.getDouble(1); + Assert.assertEquals(10.0, result1, 0.01); + Assert.assertFalse(resultSet.next()); + } catch (SQLException throwable) { + fail(throwable.getMessage()); + } + } + + @Test + public void testIQR2() { + String sqlStr = "select iqr(d1.s2) from root.vehicle"; + try (Connection connection = EnvFactory.getEnv().getConnection(); + Statement statement = connection.createStatement()) { + ResultSet resultSet = statement.executeQuery(sqlStr); + resultSet.next(); + double result1 = resultSet.getDouble(1); + Assert.assertEquals(10.0, result1, 0.01); + Assert.assertFalse(resultSet.next()); + } catch (SQLException throwable) { + fail(throwable.getMessage()); + } + } + + @Test + public void testIQR3() { + String sqlStr = "select iqr(d1.s3) from root.vehicle"; + try (Connection connection = EnvFactory.getEnv().getConnection(); + Statement statement = connection.createStatement()) { + ResultSet resultSet = statement.executeQuery(sqlStr); + resultSet.next(); + double result1 = resultSet.getDouble(1); + Assert.assertEquals(10.0, result1, 0.01); + Assert.assertFalse(resultSet.next()); + } catch (SQLException throwable) { + fail(throwable.getMessage()); + } + } + + @Test + public void testIQR4() { + String sqlStr = "select iqr(d1.s4) from root.vehicle"; + try (Connection connection = EnvFactory.getEnv().getConnection(); + Statement statement = connection.createStatement()) { + ResultSet resultSet = statement.executeQuery(sqlStr); + resultSet.next(); + double result1 = resultSet.getDouble(1); + Assert.assertEquals(10.0, result1, 0.01); + Assert.assertFalse(resultSet.next()); + } catch (SQLException throwable) { + fail(throwable.getMessage()); + } + } + + @Test + public void testKSigma1() { + String sqlStr = "select ksigma(d2.s1,\"k\"=\"1.0\") from root.vehicle"; + try (Connection connection = EnvFactory.getEnv().getConnection(); + Statement statement = connection.createStatement()) { + ResultSet resultSet = statement.executeQuery(sqlStr); + resultSet.next(); + double result1 = resultSet.getDouble(1); + resultSet.next(); + double result2 = resultSet.getDouble(1); + resultSet.next(); + double result3 = resultSet.getDouble(1); + resultSet.next(); + double result4 = resultSet.getDouble(1); + Assert.assertEquals(0.0, result1, 0.01); + Assert.assertEquals(50.0, result2, 0.01); + Assert.assertEquals(50.0, result3, 0.01); + Assert.assertEquals(0.0, result4, 0.01); + Assert.assertFalse(resultSet.next()); + } catch (SQLException throwable) { + fail(throwable.getMessage()); + } + } + + @Test + public void testKSigma2() { + String sqlStr = "select ksigma(d2.s2,\"k\"=\"1.0\") from root.vehicle"; + try (Connection connection = EnvFactory.getEnv().getConnection(); + Statement statement = connection.createStatement()) { + ResultSet resultSet = statement.executeQuery(sqlStr); + resultSet.next(); + double result1 = resultSet.getDouble(1); + resultSet.next(); + double result2 = resultSet.getDouble(1); + resultSet.next(); + double result3 = resultSet.getDouble(1); + resultSet.next(); + double result4 = resultSet.getDouble(1); + Assert.assertEquals(0.0, result1, 0.01); + Assert.assertEquals(50.0, result2, 0.01); + Assert.assertEquals(50.0, result3, 0.01); + Assert.assertEquals(0.0, result4, 0.01); + Assert.assertFalse(resultSet.next()); + } catch (SQLException throwable) { + fail(throwable.getMessage()); + } + } + + @Test + public void testKSigma3() { + String sqlStr = "select ksigma(d2.s3,\"k\"=\"1.0\") from root.vehicle"; + try (Connection connection = EnvFactory.getEnv().getConnection(); + Statement statement = connection.createStatement()) { + ResultSet resultSet = statement.executeQuery(sqlStr); + resultSet.next(); + double result1 = resultSet.getDouble(1); + resultSet.next(); + double result2 = resultSet.getDouble(1); + resultSet.next(); + double result3 = resultSet.getDouble(1); + resultSet.next(); + double result4 = resultSet.getDouble(1); + Assert.assertEquals(0.0, result1, 0.01); + Assert.assertEquals(50.0, result2, 0.01); + Assert.assertEquals(50.0, result3, 0.01); + Assert.assertEquals(0.0, result4, 0.01); + Assert.assertFalse(resultSet.next()); + } catch (SQLException throwable) { + fail(throwable.getMessage()); + } + } + + @Test + public void testKSigma4() { + String sqlStr = "select ksigma(d2.s4,\"k\"=\"1.0\") from root.vehicle"; + try (Connection connection = EnvFactory.getEnv().getConnection(); + Statement statement = connection.createStatement()) { + ResultSet resultSet = statement.executeQuery(sqlStr); + resultSet.next(); + double result1 = resultSet.getDouble(1); + resultSet.next(); + double result2 = resultSet.getDouble(1); + resultSet.next(); + double result3 = resultSet.getDouble(1); + resultSet.next(); + double result4 = resultSet.getDouble(1); + Assert.assertEquals(0.0, result1, 0.01); + Assert.assertEquals(50.0, result2, 0.01); + Assert.assertEquals(50.0, result3, 0.01); + Assert.assertEquals(0.0, result4, 0.01); + Assert.assertFalse(resultSet.next()); + } catch (SQLException throwable) { + fail(throwable.getMessage()); + } + } + + @Test + public void testMissDetect1() { + String sqlStr = "select missdetect(d4.s1,'minlen'='10') from root.vehicle"; + try (Connection connection = EnvFactory.getEnv().getConnection(); + Statement statement = connection.createStatement()) { + ResultSet resultSet = statement.executeQuery(sqlStr); + resultSet.next(); + boolean result1 = resultSet.getBoolean(1); + resultSet.next(); + boolean result2 = resultSet.getBoolean(1); + resultSet.next(); + boolean result3 = resultSet.getBoolean(1); + resultSet.next(); + boolean result4 = resultSet.getBoolean(1); + resultSet.next(); + boolean result5 = resultSet.getBoolean(1); + resultSet.next(); + boolean result6 = resultSet.getBoolean(1); + resultSet.next(); + boolean result7 = resultSet.getBoolean(1); + resultSet.next(); + boolean result8 = resultSet.getBoolean(1); + resultSet.next(); + boolean result9 = resultSet.getBoolean(1); + resultSet.next(); + boolean result10 = resultSet.getBoolean(1); + resultSet.next(); + boolean result11 = resultSet.getBoolean(1); + resultSet.next(); + boolean result12 = resultSet.getBoolean(1); + resultSet.next(); + boolean result13 = resultSet.getBoolean(1); + resultSet.next(); + boolean result14 = resultSet.getBoolean(1); + resultSet.next(); + boolean result15 = resultSet.getBoolean(1); + resultSet.next(); + boolean result16 = resultSet.getBoolean(1); + resultSet.next(); + boolean result17 = resultSet.getBoolean(1); + resultSet.next(); + boolean result18 = resultSet.getBoolean(1); + resultSet.next(); + boolean result19 = resultSet.getBoolean(1); + resultSet.next(); + boolean result20 = resultSet.getBoolean(1); + Assert.assertFalse(result1); + Assert.assertFalse(result2); + Assert.assertFalse(result3); + Assert.assertFalse(result4); + Assert.assertTrue(result5); + Assert.assertTrue(result6); + Assert.assertTrue(result7); + Assert.assertTrue(result8); + Assert.assertTrue(result9); + Assert.assertTrue(result10); + Assert.assertTrue(result11); + Assert.assertTrue(result12); + Assert.assertTrue(result13); + Assert.assertTrue(result14); + Assert.assertTrue(result15); + Assert.assertFalse(result16); + Assert.assertFalse(result17); + Assert.assertFalse(result18); + Assert.assertFalse(result19); + Assert.assertFalse(result20); + Assert.assertFalse(resultSet.next()); + } catch (SQLException throwable) { + fail(throwable.getMessage()); + } + } + + @Test + public void testMissDetect2() { + String sqlStr = "select missdetect(d4.s2,'minlen'='10') from root.vehicle"; + try (Connection connection = EnvFactory.getEnv().getConnection(); + Statement statement = connection.createStatement()) { + ResultSet resultSet = statement.executeQuery(sqlStr); + resultSet.next(); + boolean result1 = resultSet.getBoolean(1); + resultSet.next(); + boolean result2 = resultSet.getBoolean(1); + resultSet.next(); + boolean result3 = resultSet.getBoolean(1); + resultSet.next(); + boolean result4 = resultSet.getBoolean(1); + resultSet.next(); + boolean result5 = resultSet.getBoolean(1); + resultSet.next(); + boolean result6 = resultSet.getBoolean(1); + resultSet.next(); + boolean result7 = resultSet.getBoolean(1); + resultSet.next(); + boolean result8 = resultSet.getBoolean(1); + resultSet.next(); + boolean result9 = resultSet.getBoolean(1); + resultSet.next(); + boolean result10 = resultSet.getBoolean(1); + resultSet.next(); + boolean result11 = resultSet.getBoolean(1); + resultSet.next(); + boolean result12 = resultSet.getBoolean(1); + resultSet.next(); + boolean result13 = resultSet.getBoolean(1); + resultSet.next(); + boolean result14 = resultSet.getBoolean(1); + resultSet.next(); + boolean result15 = resultSet.getBoolean(1); + resultSet.next(); + boolean result16 = resultSet.getBoolean(1); + resultSet.next(); + boolean result17 = resultSet.getBoolean(1); + resultSet.next(); + boolean result18 = resultSet.getBoolean(1); + resultSet.next(); + boolean result19 = resultSet.getBoolean(1); + resultSet.next(); + boolean result20 = resultSet.getBoolean(1); + Assert.assertFalse(result1); + Assert.assertFalse(result2); + Assert.assertFalse(result3); + Assert.assertFalse(result4); + Assert.assertTrue(result5); + Assert.assertTrue(result6); + Assert.assertTrue(result7); + Assert.assertTrue(result8); + Assert.assertTrue(result9); + Assert.assertTrue(result10); + Assert.assertTrue(result11); + Assert.assertTrue(result12); + Assert.assertTrue(result13); + Assert.assertTrue(result14); + Assert.assertTrue(result15); + Assert.assertFalse(result16); + Assert.assertFalse(result17); + Assert.assertFalse(result18); + Assert.assertFalse(result19); + Assert.assertFalse(result20); + Assert.assertFalse(resultSet.next()); + } catch (SQLException throwable) { + fail(throwable.getMessage()); + } + } + + @Test + public void testMissDetect3() { + String sqlStr = "select missdetect(d4.s3,'minlen'='10') from root.vehicle"; + try (Connection connection = EnvFactory.getEnv().getConnection(); + Statement statement = connection.createStatement()) { + ResultSet resultSet = statement.executeQuery(sqlStr); + resultSet.next(); + boolean result1 = resultSet.getBoolean(1); + resultSet.next(); + boolean result2 = resultSet.getBoolean(1); + resultSet.next(); + boolean result3 = resultSet.getBoolean(1); + resultSet.next(); + boolean result4 = resultSet.getBoolean(1); + resultSet.next(); + boolean result5 = resultSet.getBoolean(1); + resultSet.next(); + boolean result6 = resultSet.getBoolean(1); + resultSet.next(); + boolean result7 = resultSet.getBoolean(1); + resultSet.next(); + boolean result8 = resultSet.getBoolean(1); + resultSet.next(); + boolean result9 = resultSet.getBoolean(1); + resultSet.next(); + boolean result10 = resultSet.getBoolean(1); + resultSet.next(); + boolean result11 = resultSet.getBoolean(1); + resultSet.next(); + boolean result12 = resultSet.getBoolean(1); + resultSet.next(); + boolean result13 = resultSet.getBoolean(1); + resultSet.next(); + boolean result14 = resultSet.getBoolean(1); + resultSet.next(); + boolean result15 = resultSet.getBoolean(1); + resultSet.next(); + boolean result16 = resultSet.getBoolean(1); + resultSet.next(); + boolean result17 = resultSet.getBoolean(1); + resultSet.next(); + boolean result18 = resultSet.getBoolean(1); + resultSet.next(); + boolean result19 = resultSet.getBoolean(1); + resultSet.next(); + boolean result20 = resultSet.getBoolean(1); + Assert.assertFalse(result1); + Assert.assertFalse(result2); + Assert.assertFalse(result3); + Assert.assertFalse(result4); + Assert.assertTrue(result5); + Assert.assertTrue(result6); + Assert.assertTrue(result7); + Assert.assertTrue(result8); + Assert.assertTrue(result9); + Assert.assertTrue(result10); + Assert.assertTrue(result11); + Assert.assertTrue(result12); + Assert.assertTrue(result13); + Assert.assertTrue(result14); + Assert.assertTrue(result15); + Assert.assertFalse(result16); + Assert.assertFalse(result17); + Assert.assertFalse(result18); + Assert.assertFalse(result19); + Assert.assertFalse(result20); + Assert.assertFalse(resultSet.next()); + } catch (SQLException throwable) { + fail(throwable.getMessage()); + } + } + + @Test + public void testMissDetect4() { + String sqlStr = "select missdetect(d4.s4,'minlen'='10') from root.vehicle"; + try (Connection connection = EnvFactory.getEnv().getConnection(); + Statement statement = connection.createStatement()) { + ResultSet resultSet = statement.executeQuery(sqlStr); + resultSet.next(); + boolean result1 = resultSet.getBoolean(1); + resultSet.next(); + boolean result2 = resultSet.getBoolean(1); + resultSet.next(); + boolean result3 = resultSet.getBoolean(1); + resultSet.next(); + boolean result4 = resultSet.getBoolean(1); + resultSet.next(); + boolean result5 = resultSet.getBoolean(1); + resultSet.next(); + boolean result6 = resultSet.getBoolean(1); + resultSet.next(); + boolean result7 = resultSet.getBoolean(1); + resultSet.next(); + boolean result8 = resultSet.getBoolean(1); + resultSet.next(); + boolean result9 = resultSet.getBoolean(1); + resultSet.next(); + boolean result10 = resultSet.getBoolean(1); + resultSet.next(); + boolean result11 = resultSet.getBoolean(1); + resultSet.next(); + boolean result12 = resultSet.getBoolean(1); + resultSet.next(); + boolean result13 = resultSet.getBoolean(1); + resultSet.next(); + boolean result14 = resultSet.getBoolean(1); + resultSet.next(); + boolean result15 = resultSet.getBoolean(1); + resultSet.next(); + boolean result16 = resultSet.getBoolean(1); + resultSet.next(); + boolean result17 = resultSet.getBoolean(1); + resultSet.next(); + boolean result18 = resultSet.getBoolean(1); + resultSet.next(); + boolean result19 = resultSet.getBoolean(1); + resultSet.next(); + boolean result20 = resultSet.getBoolean(1); + Assert.assertFalse(result1); + Assert.assertFalse(result2); + Assert.assertFalse(result3); + Assert.assertFalse(result4); + Assert.assertTrue(result5); + Assert.assertTrue(result6); + Assert.assertTrue(result7); + Assert.assertTrue(result8); + Assert.assertTrue(result9); + Assert.assertTrue(result10); + Assert.assertTrue(result11); + Assert.assertTrue(result12); + Assert.assertTrue(result13); + Assert.assertTrue(result14); + Assert.assertTrue(result15); + Assert.assertFalse(result16); + Assert.assertFalse(result17); + Assert.assertFalse(result18); + Assert.assertFalse(result19); + Assert.assertFalse(result20); + Assert.assertFalse(resultSet.next()); + } catch (SQLException throwable) { + fail(throwable.getMessage()); + } + } + + @Test + public void testLOF1() { + String sqlStr = "select lof(d3.s1,d3.s5) from root.vehicle"; + try (Connection connection = EnvFactory.getEnv().getConnection(); + Statement statement = connection.createStatement()) { + ResultSet resultSet = statement.executeQuery(sqlStr); + resultSet.next(); + double result1 = resultSet.getDouble(1); + resultSet.next(); + double result2 = resultSet.getDouble(1); + resultSet.next(); + double result3 = resultSet.getDouble(1); + resultSet.next(); + double result4 = resultSet.getDouble(1); + resultSet.next(); + double result5 = resultSet.getDouble(1); + resultSet.next(); + double result6 = resultSet.getDouble(1); + resultSet.next(); + double result7 = resultSet.getDouble(1); + resultSet.next(); + double result8 = resultSet.getDouble(1); + Assert.assertEquals(3.8274824267668244, result1, 0.01); + Assert.assertEquals(3.0117631741126156, result2, 0.01); + Assert.assertEquals(2.838155437762879, result3, 0.01); + Assert.assertEquals(3.0117631741126156, result4, 0.01); + Assert.assertEquals(2.73518261244453, result5, 0.01); + Assert.assertEquals(2.371440975708148, result6, 0.01); + Assert.assertEquals(2.73518261244453, result7, 0.01); + Assert.assertEquals(1.7561416374270742, result8, 0.01); + Assert.assertFalse(resultSet.next()); + } catch (SQLException throwable) { + fail(throwable.getMessage()); + } + } + + @Test + public void testLOF2() { + String sqlStr = "select lof(d3.s2,d3.s6) from root.vehicle"; + try (Connection connection = EnvFactory.getEnv().getConnection(); + Statement statement = connection.createStatement()) { + ResultSet resultSet = statement.executeQuery(sqlStr); + resultSet.next(); + double result1 = resultSet.getDouble(1); + resultSet.next(); + double result2 = resultSet.getDouble(1); + resultSet.next(); + double result3 = resultSet.getDouble(1); + resultSet.next(); + double result4 = resultSet.getDouble(1); + resultSet.next(); + double result5 = resultSet.getDouble(1); + resultSet.next(); + double result6 = resultSet.getDouble(1); + resultSet.next(); + double result7 = resultSet.getDouble(1); + resultSet.next(); + double result8 = resultSet.getDouble(1); + Assert.assertEquals(3.8274824267668244, result1, 0.01); + Assert.assertEquals(3.0117631741126156, result2, 0.01); + Assert.assertEquals(2.838155437762879, result3, 0.01); + Assert.assertEquals(3.0117631741126156, result4, 0.01); + Assert.assertEquals(2.73518261244453, result5, 0.01); + Assert.assertEquals(2.371440975708148, result6, 0.01); + Assert.assertEquals(2.73518261244453, result7, 0.01); + Assert.assertEquals(1.7561416374270742, result8, 0.01); + Assert.assertFalse(resultSet.next()); + } catch (SQLException throwable) { + fail(throwable.getMessage()); + } + } + + @Test + public void testLOF3() { + String sqlStr = "select lof(d3.s3,d3.s7) from root.vehicle"; + try (Connection connection = EnvFactory.getEnv().getConnection(); + Statement statement = connection.createStatement()) { + ResultSet resultSet = statement.executeQuery(sqlStr); + resultSet.next(); + double result1 = resultSet.getDouble(1); + resultSet.next(); + double result2 = resultSet.getDouble(1); + resultSet.next(); + double result3 = resultSet.getDouble(1); + resultSet.next(); + double result4 = resultSet.getDouble(1); + resultSet.next(); + double result5 = resultSet.getDouble(1); + resultSet.next(); + double result6 = resultSet.getDouble(1); + resultSet.next(); + double result7 = resultSet.getDouble(1); + resultSet.next(); + double result8 = resultSet.getDouble(1); + Assert.assertEquals(3.8274824267668244, result1, 0.01); + Assert.assertEquals(3.0117631741126156, result2, 0.01); + Assert.assertEquals(2.838155437762879, result3, 0.01); + Assert.assertEquals(3.0117631741126156, result4, 0.01); + Assert.assertEquals(2.73518261244453, result5, 0.01); + Assert.assertEquals(2.371440975708148, result6, 0.01); + Assert.assertEquals(2.73518261244453, result7, 0.01); + Assert.assertEquals(1.7561416374270742, result8, 0.01); + Assert.assertFalse(resultSet.next()); + } catch (SQLException throwable) { + fail(throwable.getMessage()); + } + } + + @Test + public void testLOF4() { + String sqlStr = "select lof(d3.s4,d3.s8) from root.vehicle"; + try (Connection connection = EnvFactory.getEnv().getConnection(); + Statement statement = connection.createStatement()) { + ResultSet resultSet = statement.executeQuery(sqlStr); + resultSet.next(); + double result1 = resultSet.getDouble(1); + resultSet.next(); + double result2 = resultSet.getDouble(1); + resultSet.next(); + double result3 = resultSet.getDouble(1); + resultSet.next(); + double result4 = resultSet.getDouble(1); + resultSet.next(); + double result5 = resultSet.getDouble(1); + resultSet.next(); + double result6 = resultSet.getDouble(1); + resultSet.next(); + double result7 = resultSet.getDouble(1); + resultSet.next(); + double result8 = resultSet.getDouble(1); + Assert.assertEquals(3.8274824267668244, result1, 0.01); + Assert.assertEquals(3.0117631741126156, result2, 0.01); + Assert.assertEquals(2.838155437762879, result3, 0.01); + Assert.assertEquals(3.0117631741126156, result4, 0.01); + Assert.assertEquals(2.73518261244453, result5, 0.01); + Assert.assertEquals(2.371440975708148, result6, 0.01); + Assert.assertEquals(2.73518261244453, result7, 0.01); + Assert.assertEquals(1.7561416374270742, result8, 0.01); + Assert.assertFalse(resultSet.next()); + } catch (SQLException throwable) { + fail(throwable.getMessage()); + } + } + + @Test + public void testRange1() { + String sqlStr = + "select range(d1.s1,\"lower_bound\"=\"-5.0\",\"upper_bound\"=\"5.0\") from root.vehicle"; + try (Connection connection = EnvFactory.getEnv().getConnection(); + Statement statement = connection.createStatement()) { + ResultSet resultSet = statement.executeQuery(sqlStr); + resultSet.next(); + double result1 = resultSet.getDouble(1); + Assert.assertEquals(10.0, result1, 0.01); + Assert.assertFalse(resultSet.next()); + } catch (SQLException throwable) { + fail(throwable.getMessage()); + } + } + + @Test + public void testRange2() { + String sqlStr = + "select range(d1.s2,\"lower_bound\"=\"-5.0\",\"upper_bound\"=\"5.0\") from root.vehicle"; + try (Connection connection = EnvFactory.getEnv().getConnection(); + Statement statement = connection.createStatement()) { + ResultSet resultSet = statement.executeQuery(sqlStr); + resultSet.next(); + double result1 = resultSet.getDouble(1); + Assert.assertEquals(10.0, result1, 0.01); + Assert.assertFalse(resultSet.next()); + } catch (SQLException throwable) { + fail(throwable.getMessage()); + } + } + + @Test + public void testRange3() { + String sqlStr = + "select range(d1.s3,\"lower_bound\"=\"-5.0\",\"upper_bound\"=\"5.0\") from root.vehicle"; + try (Connection connection = EnvFactory.getEnv().getConnection(); + Statement statement = connection.createStatement()) { + ResultSet resultSet = statement.executeQuery(sqlStr); + resultSet.next(); + double result1 = resultSet.getDouble(1); + Assert.assertEquals(10.0, result1, 0.01); + Assert.assertFalse(resultSet.next()); + } catch (SQLException throwable) { + fail(throwable.getMessage()); + } + } + + @Test + public void testRange4() { + String sqlStr = + "select range(d1.s4,\"lower_bound\"=\"-5.0\",\"upper_bound\"=\"5.0\") from root.vehicle"; + try (Connection connection = EnvFactory.getEnv().getConnection(); + Statement statement = connection.createStatement()) { + ResultSet resultSet = statement.executeQuery(sqlStr); + resultSet.next(); + double result1 = resultSet.getDouble(1); + Assert.assertEquals(10.0, result1, 0.01); + Assert.assertFalse(resultSet.next()); + } catch (SQLException throwable) { + fail(throwable.getMessage()); + } + } + + @Test + public void testTwoSidedFileter1() { + String sqlStr = "select TwoSidedFilter(d6.s1, 'len'='3', 'threshold'='0.3') from root.vehicle"; + try (Connection connection = EnvFactory.getEnv().getConnection(); + Statement statement = connection.createStatement()) { + ResultSet resultSet = statement.executeQuery(sqlStr); + resultSet.next(); + double result1 = resultSet.getDouble(1); + resultSet.next(); + double result2 = resultSet.getDouble(1); + resultSet.next(); + double result3 = resultSet.getDouble(1); + resultSet.next(); + double result4 = resultSet.getDouble(1); + resultSet.next(); + double result5 = resultSet.getDouble(1); + resultSet.next(); + double result6 = resultSet.getDouble(1); + resultSet.next(); + double result7 = resultSet.getDouble(1); + resultSet.next(); + double result8 = resultSet.getDouble(1); + resultSet.next(); + double result9 = resultSet.getDouble(1); + resultSet.next(); + double result10 = resultSet.getDouble(1); + resultSet.next(); + double result11 = resultSet.getDouble(1); + resultSet.next(); + double result12 = resultSet.getDouble(1); + resultSet.next(); + double result13 = resultSet.getDouble(1); + resultSet.next(); + double result14 = resultSet.getDouble(1); + resultSet.next(); + double result15 = resultSet.getDouble(1); + resultSet.next(); + double result16 = resultSet.getDouble(1); + resultSet.next(); + double result17 = resultSet.getDouble(1); + resultSet.next(); + double result18 = resultSet.getDouble(1); + resultSet.next(); + double result19 = resultSet.getDouble(1); + resultSet.next(); + double result20 = resultSet.getDouble(1); + resultSet.next(); + double result21 = resultSet.getDouble(1); + resultSet.next(); + double result22 = resultSet.getDouble(1); + resultSet.next(); + double result23 = resultSet.getDouble(1); + Assert.assertEquals(2002.0, result1, 0.01); + Assert.assertEquals(1946.0, result2, 0.01); + Assert.assertEquals(1958.0, result3, 0.01); + Assert.assertEquals(2012.0, result4, 0.01); + Assert.assertEquals(2051.0, result5, 0.01); + Assert.assertEquals(1898.0, result6, 0.01); + Assert.assertEquals(2014.0, result7, 0.01); + Assert.assertEquals(2052.0, result8, 0.01); + Assert.assertEquals(1935.0, result9, 0.01); + Assert.assertEquals(1901.0, result10, 0.01); + Assert.assertEquals(1972.0, result11, 0.01); + Assert.assertEquals(1969.0, result12, 0.01); + Assert.assertEquals(1984.0, result13, 0.01); + Assert.assertEquals(2018.0, result14, 0.01); + Assert.assertEquals(1023.0, result15, 0.01); + Assert.assertEquals(1056.0, result16, 0.01); + Assert.assertEquals(978.0, result17, 0.01); + Assert.assertEquals(1050.0, result18, 0.01); + Assert.assertEquals(1123.0, result19, 0.01); + Assert.assertEquals(1150.0, result20, 0.01); + Assert.assertEquals(1134.0, result21, 0.01); + Assert.assertEquals(950.0, result22, 0.01); + Assert.assertEquals(1059.0, result23, 0.01); + Assert.assertFalse(resultSet.next()); + } catch (SQLException throwable) { + fail(throwable.getMessage()); + } + } + + @Test + public void testTwoSidedFileter2() { + String sqlStr = "select TwoSidedFilter(d6.s2, 'len'='3', 'threshold'='0.3') from root.vehicle"; + try (Connection connection = EnvFactory.getEnv().getConnection(); + Statement statement = connection.createStatement()) { + ResultSet resultSet = statement.executeQuery(sqlStr); + resultSet.next(); + double result1 = resultSet.getDouble(1); + resultSet.next(); + double result2 = resultSet.getDouble(1); + resultSet.next(); + double result3 = resultSet.getDouble(1); + resultSet.next(); + double result4 = resultSet.getDouble(1); + resultSet.next(); + double result5 = resultSet.getDouble(1); + resultSet.next(); + double result6 = resultSet.getDouble(1); + resultSet.next(); + double result7 = resultSet.getDouble(1); + resultSet.next(); + double result8 = resultSet.getDouble(1); + resultSet.next(); + double result9 = resultSet.getDouble(1); + resultSet.next(); + double result10 = resultSet.getDouble(1); + resultSet.next(); + double result11 = resultSet.getDouble(1); + resultSet.next(); + double result12 = resultSet.getDouble(1); + resultSet.next(); + double result13 = resultSet.getDouble(1); + resultSet.next(); + double result14 = resultSet.getDouble(1); + resultSet.next(); + double result15 = resultSet.getDouble(1); + resultSet.next(); + double result16 = resultSet.getDouble(1); + resultSet.next(); + double result17 = resultSet.getDouble(1); + resultSet.next(); + double result18 = resultSet.getDouble(1); + resultSet.next(); + double result19 = resultSet.getDouble(1); + resultSet.next(); + double result20 = resultSet.getDouble(1); + resultSet.next(); + double result21 = resultSet.getDouble(1); + resultSet.next(); + double result22 = resultSet.getDouble(1); + resultSet.next(); + double result23 = resultSet.getDouble(1); + Assert.assertEquals(2002.0, result1, 0.01); + Assert.assertEquals(1946.0, result2, 0.01); + Assert.assertEquals(1958.0, result3, 0.01); + Assert.assertEquals(2012.0, result4, 0.01); + Assert.assertEquals(2051.0, result5, 0.01); + Assert.assertEquals(1898.0, result6, 0.01); + Assert.assertEquals(2014.0, result7, 0.01); + Assert.assertEquals(2052.0, result8, 0.01); + Assert.assertEquals(1935.0, result9, 0.01); + Assert.assertEquals(1901.0, result10, 0.01); + Assert.assertEquals(1972.0, result11, 0.01); + Assert.assertEquals(1969.0, result12, 0.01); + Assert.assertEquals(1984.0, result13, 0.01); + Assert.assertEquals(2018.0, result14, 0.01); + Assert.assertEquals(1023.0, result15, 0.01); + Assert.assertEquals(1056.0, result16, 0.01); + Assert.assertEquals(978.0, result17, 0.01); + Assert.assertEquals(1050.0, result18, 0.01); + Assert.assertEquals(1123.0, result19, 0.01); + Assert.assertEquals(1150.0, result20, 0.01); + Assert.assertEquals(1134.0, result21, 0.01); + Assert.assertEquals(950.0, result22, 0.01); + Assert.assertEquals(1059.0, result23, 0.01); + Assert.assertFalse(resultSet.next()); + } catch (SQLException throwable) { + fail(throwable.getMessage()); + } + } + + @Test + public void testTwoSidedFileter3() { + String sqlStr = "select TwoSidedFilter(d6.s3, 'len'='3', 'threshold'='0.3') from root.vehicle"; + try (Connection connection = EnvFactory.getEnv().getConnection(); + Statement statement = connection.createStatement()) { + ResultSet resultSet = statement.executeQuery(sqlStr); + resultSet.next(); + double result1 = resultSet.getDouble(1); + resultSet.next(); + double result2 = resultSet.getDouble(1); + resultSet.next(); + double result3 = resultSet.getDouble(1); + resultSet.next(); + double result4 = resultSet.getDouble(1); + resultSet.next(); + double result5 = resultSet.getDouble(1); + resultSet.next(); + double result6 = resultSet.getDouble(1); + resultSet.next(); + double result7 = resultSet.getDouble(1); + resultSet.next(); + double result8 = resultSet.getDouble(1); + resultSet.next(); + double result9 = resultSet.getDouble(1); + resultSet.next(); + double result10 = resultSet.getDouble(1); + resultSet.next(); + double result11 = resultSet.getDouble(1); + resultSet.next(); + double result12 = resultSet.getDouble(1); + resultSet.next(); + double result13 = resultSet.getDouble(1); + resultSet.next(); + double result14 = resultSet.getDouble(1); + resultSet.next(); + double result15 = resultSet.getDouble(1); + resultSet.next(); + double result16 = resultSet.getDouble(1); + resultSet.next(); + double result17 = resultSet.getDouble(1); + resultSet.next(); + double result18 = resultSet.getDouble(1); + resultSet.next(); + double result19 = resultSet.getDouble(1); + resultSet.next(); + double result20 = resultSet.getDouble(1); + resultSet.next(); + double result21 = resultSet.getDouble(1); + resultSet.next(); + double result22 = resultSet.getDouble(1); + resultSet.next(); + double result23 = resultSet.getDouble(1); + Assert.assertEquals(2002.0, result1, 0.01); + Assert.assertEquals(1946.0, result2, 0.01); + Assert.assertEquals(1958.0, result3, 0.01); + Assert.assertEquals(2012.0, result4, 0.01); + Assert.assertEquals(2051.0, result5, 0.01); + Assert.assertEquals(1898.0, result6, 0.01); + Assert.assertEquals(2014.0, result7, 0.01); + Assert.assertEquals(2052.0, result8, 0.01); + Assert.assertEquals(1935.0, result9, 0.01); + Assert.assertEquals(1901.0, result10, 0.01); + Assert.assertEquals(1972.0, result11, 0.01); + Assert.assertEquals(1969.0, result12, 0.01); + Assert.assertEquals(1984.0, result13, 0.01); + Assert.assertEquals(2018.0, result14, 0.01); + Assert.assertEquals(1023.0, result15, 0.01); + Assert.assertEquals(1056.0, result16, 0.01); + Assert.assertEquals(978.0, result17, 0.01); + Assert.assertEquals(1050.0, result18, 0.01); + Assert.assertEquals(1123.0, result19, 0.01); + Assert.assertEquals(1150.0, result20, 0.01); + Assert.assertEquals(1134.0, result21, 0.01); + Assert.assertEquals(950.0, result22, 0.01); + Assert.assertEquals(1059.0, result23, 0.01); + Assert.assertFalse(resultSet.next()); + } catch (SQLException throwable) { + fail(throwable.getMessage()); + } + } + + @Test + public void testTwoSidedFileter4() { + String sqlStr = "select TwoSidedFilter(d6.s4, 'len'='3', 'threshold'='0.3') from root.vehicle"; + try (Connection connection = EnvFactory.getEnv().getConnection(); + Statement statement = connection.createStatement()) { + ResultSet resultSet = statement.executeQuery(sqlStr); + resultSet.next(); + double result1 = resultSet.getDouble(1); + resultSet.next(); + double result2 = resultSet.getDouble(1); + resultSet.next(); + double result3 = resultSet.getDouble(1); + resultSet.next(); + double result4 = resultSet.getDouble(1); + resultSet.next(); + double result5 = resultSet.getDouble(1); + resultSet.next(); + double result6 = resultSet.getDouble(1); + resultSet.next(); + double result7 = resultSet.getDouble(1); + resultSet.next(); + double result8 = resultSet.getDouble(1); + resultSet.next(); + double result9 = resultSet.getDouble(1); + resultSet.next(); + double result10 = resultSet.getDouble(1); + resultSet.next(); + double result11 = resultSet.getDouble(1); + resultSet.next(); + double result12 = resultSet.getDouble(1); + resultSet.next(); + double result13 = resultSet.getDouble(1); + resultSet.next(); + double result14 = resultSet.getDouble(1); + resultSet.next(); + double result15 = resultSet.getDouble(1); + resultSet.next(); + double result16 = resultSet.getDouble(1); + resultSet.next(); + double result17 = resultSet.getDouble(1); + resultSet.next(); + double result18 = resultSet.getDouble(1); + resultSet.next(); + double result19 = resultSet.getDouble(1); + resultSet.next(); + double result20 = resultSet.getDouble(1); + resultSet.next(); + double result21 = resultSet.getDouble(1); + resultSet.next(); + double result22 = resultSet.getDouble(1); + resultSet.next(); + double result23 = resultSet.getDouble(1); + Assert.assertEquals(2002.0, result1, 0.01); + Assert.assertEquals(1946.0, result2, 0.01); + Assert.assertEquals(1958.0, result3, 0.01); + Assert.assertEquals(2012.0, result4, 0.01); + Assert.assertEquals(2051.0, result5, 0.01); + Assert.assertEquals(1898.0, result6, 0.01); + Assert.assertEquals(2014.0, result7, 0.01); + Assert.assertEquals(2052.0, result8, 0.01); + Assert.assertEquals(1935.0, result9, 0.01); + Assert.assertEquals(1901.0, result10, 0.01); + Assert.assertEquals(1972.0, result11, 0.01); + Assert.assertEquals(1969.0, result12, 0.01); + Assert.assertEquals(1984.0, result13, 0.01); + Assert.assertEquals(2018.0, result14, 0.01); + Assert.assertEquals(1023.0, result15, 0.01); + Assert.assertEquals(1056.0, result16, 0.01); + Assert.assertEquals(978.0, result17, 0.01); + Assert.assertEquals(1050.0, result18, 0.01); + Assert.assertEquals(1123.0, result19, 0.01); + Assert.assertEquals(1150.0, result20, 0.01); + Assert.assertEquals(1134.0, result21, 0.01); + Assert.assertEquals(950.0, result22, 0.01); + Assert.assertEquals(1059.0, result23, 0.01); + Assert.assertFalse(resultSet.next()); + } catch (SQLException throwable) { + fail(throwable.getMessage()); + } + } +} From 8f81a4334d91734996e995a76c745e6be38458ca Mon Sep 17 00:00:00 2001 From: Steve Yurong Su Date: Sat, 21 May 2022 04:06:12 +0800 Subject: [PATCH 065/436] [IOTDB-3249] Migrate udf package to node-commons and redesign the udf package (#5977) 1. Migrate part of the udf package to node-commons for further mpp cluster development 2. Redesign the package structure of udf for better understanding 3. Move the expression package and the transformer package into mpp package --- .../query/ClusterPhysicalGeneratorTest.java | 4 +- .../last/ClusterLastQueryExecutorTest.java | 2 +- .../org/apache/iotdb/udf/UDTFExample.java | 14 +-- .../db/query/udf/example/Accumulator.java | 22 ++-- .../iotdb/db/query/udf/example/Adder.java | 14 +-- .../iotdb/db/query/udf/example/Counter.java | 18 ++-- .../iotdb/db/query/udf/example/Max.java | 14 +-- .../db/query/udf/example/Multiplier.java | 14 +-- .../SlidingSizeWindowConstructorTester0.java | 12 +-- .../SlidingSizeWindowConstructorTester1.java | 14 +-- .../SlidingTimeWindowConstructionTester.java | 16 +-- .../db/query/udf/example/TerminateTester.java | 12 +-- .../db/query/udf/example/ValidateTester.java | 10 +- .../db/query/udf/example/WindowStartEnd.java | 14 +-- .../IoTDBSyntaxConventionStringLiteralIT.java | 2 +- .../db/integration/IoTDBUDFManagementIT.java | 2 +- .../apache/iotdb/library/anomaly/UDTFIQR.java | 14 +-- .../iotdb/library/anomaly/UDTFKSigma.java | 14 +-- .../apache/iotdb/library/anomaly/UDTFLOF.java | 14 +-- .../iotdb/library/anomaly/UDTFMissDetect.java | 14 +-- .../iotdb/library/anomaly/UDTFRange.java | 14 +-- .../library/anomaly/UDTFTwoSidedFilter.java | 14 +-- .../library/anomaly/util/MissDetector.java | 4 +- .../library/anomaly/util/WindowDetect.java | 4 +- .../apache/iotdb/library/dmatch/UDAFCov.java | 14 +-- .../apache/iotdb/library/dmatch/UDAFDtw.java | 16 +-- .../iotdb/library/dmatch/UDAFPearson.java | 14 +-- .../iotdb/library/dmatch/UDTFPtnSym.java | 16 +-- .../iotdb/library/dmatch/UDTFXCorr.java | 14 +-- .../iotdb/library/dprofile/UDAFIntegral.java | 14 +-- .../library/dprofile/UDAFIntegralAvg.java | 14 +-- .../iotdb/library/dprofile/UDAFMad.java | 14 +-- .../iotdb/library/dprofile/UDAFMedian.java | 14 +-- .../iotdb/library/dprofile/UDAFMode.java | 14 +-- .../library/dprofile/UDAFPercentile.java | 14 +-- .../iotdb/library/dprofile/UDAFPeriod.java | 18 ++-- .../iotdb/library/dprofile/UDAFSkew.java | 14 +-- .../iotdb/library/dprofile/UDAFSpread.java | 14 +-- .../iotdb/library/dprofile/UDAFStddev.java | 14 +-- .../iotdb/library/dprofile/UDTFACF.java | 14 +-- .../iotdb/library/dprofile/UDTFDistinct.java | 14 +-- .../iotdb/library/dprofile/UDTFHistogram.java | 14 +-- .../iotdb/library/dprofile/UDTFMinMax.java | 14 +-- .../iotdb/library/dprofile/UDTFMvAvg.java | 14 +-- .../iotdb/library/dprofile/UDTFPACF.java | 14 +-- .../iotdb/library/dprofile/UDTFQLB.java | 14 +-- .../iotdb/library/dprofile/UDTFResample.java | 14 +-- .../iotdb/library/dprofile/UDTFSample.java | 20 ++-- .../iotdb/library/dprofile/UDTFSegment.java | 14 +-- .../iotdb/library/dprofile/UDTFSpline.java | 14 +-- .../iotdb/library/dprofile/UDTFZScore.java | 14 +-- .../dprofile/util/ExactOrderStatistics.java | 4 +- .../library/dquality/UDTFCompleteness.java | 14 +-- .../library/dquality/UDTFConsistency.java | 14 +-- .../library/dquality/UDTFTimeliness.java | 14 +-- .../iotdb/library/dquality/UDTFValidity.java | 14 +-- .../dquality/util/TimeSeriesQuality.java | 4 +- .../library/drepair/UDTFTimestampRepair.java | 14 +-- .../iotdb/library/drepair/UDTFValueFill.java | 14 +-- .../library/drepair/UDTFValueRepair.java | 14 +-- .../iotdb/library/drepair/util/ARFill.java | 4 +- .../library/drepair/util/LikelihoodFill.java | 2 +- .../library/drepair/util/LinearFill.java | 2 +- .../iotdb/library/drepair/util/LsGreedy.java | 2 +- .../iotdb/library/drepair/util/MAFill.java | 2 +- .../iotdb/library/drepair/util/MeanFill.java | 2 +- .../library/drepair/util/PreviousFill.java | 2 +- .../iotdb/library/drepair/util/Screen.java | 2 +- .../library/drepair/util/ScreenFill.java | 4 +- .../library/drepair/util/TimestampRepair.java | 4 +- .../iotdb/library/drepair/util/ValueFill.java | 6 +- .../library/drepair/util/ValueRepair.java | 4 +- .../iotdb/library/frequency/UDTFConv.java | 14 +-- .../iotdb/library/frequency/UDTFDWT.java | 14 +-- .../iotdb/library/frequency/UDTFDeconv.java | 14 +-- .../iotdb/library/frequency/UDTFFFT.java | 14 +-- .../iotdb/library/frequency/UDTFHighPass.java | 14 +-- .../iotdb/library/frequency/UDTFIDWT.java | 14 +-- .../iotdb/library/frequency/UDTFIFFT.java | 14 +-- .../iotdb/library/frequency/UDTFLowPass.java | 14 +-- .../iotdb/library/frequency/util/FFTUtil.java | 2 +- .../series/UDTFConsecutiveSequences.java | 14 +-- .../series/UDTFConsecutiveWindows.java | 14 +-- .../library/series/util/ConsecutiveUtil.java | 4 +- .../iotdb/library/string/UDTFRegexMatch.java | 14 +-- .../library/string/UDTFRegexReplace.java | 14 +-- .../iotdb/library/string/UDTFRegexSplit.java | 14 +-- .../iotdb/library/string/UDTFStrReplace.java | 14 +-- .../iotdb/library/util/LinearRegression.java | 2 +- .../org/apache/iotdb/library/util/Util.java | 4 +- .../apache/iotdb/commons}/udf/api/UDF.java | 8 +- .../apache/iotdb/commons}/udf/api/UDTF.java | 20 ++-- .../iotdb/commons}/udf/api/access/Row.java | 2 +- .../commons}/udf/api/access/RowIterator.java | 2 +- .../commons}/udf/api/access/RowWindow.java | 6 +- .../udf/api/collector/PointCollector.java | 21 ++-- .../customizer/config/UDFConfigurations.java | 7 +- .../customizer/config/UDTFConfigurations.java | 25 +++-- .../parameter/UDFParameterValidator.java | 14 +-- .../customizer/parameter/UDFParameters.java | 57 ++++------ .../customizer/strategy/AccessStrategy.java | 13 ++- .../strategy/RowByRowAccessStrategy.java | 12 +-- .../SlidingSizeWindowAccessStrategy.java | 19 ++-- .../SlidingTimeWindowAccessStrategy.java | 101 ++++++------------ .../UDFAttributeNotProvidedException.java | 2 +- .../udf/api/exception/UDFException.java | 2 +- ...FInputSeriesDataTypeNotValidException.java | 2 +- .../UDFInputSeriesIndexNotValidException.java | 2 +- ...UDFInputSeriesNumberNotValidException.java | 2 +- ...OutputSeriesDataTypeNotValidException.java | 2 +- .../UDFParameterNotValidException.java | 2 +- .../commons}/udf/builtin/BuiltinFunction.java | 2 +- .../iotdb/commons}/udf/builtin/UDTFAbs.java | 14 +-- .../iotdb/commons}/udf/builtin/UDTFAcos.java | 2 +- .../iotdb/commons}/udf/builtin/UDTFAsin.java | 2 +- .../iotdb/commons}/udf/builtin/UDTFAtan.java | 2 +- .../commons}/udf/builtin/UDTFBottomK.java | 27 ++++- .../iotdb/commons}/udf/builtin/UDTFCast.java | 42 +++----- .../iotdb/commons}/udf/builtin/UDTFCeil.java | 2 +- .../udf/builtin/UDTFCommonDerivative.java | 8 +- .../builtin/UDTFCommonValueDifference.java | 8 +- .../iotdb/commons}/udf/builtin/UDTFConst.java | 18 ++-- .../commons}/udf/builtin/UDTFConstE.java | 14 +-- .../commons}/udf/builtin/UDTFConstPi.java | 14 +-- .../commons}/udf/builtin/UDTFContains.java | 18 ++-- .../udf/builtin/UDTFContinuouslySatisfy.java | 20 ++-- .../iotdb/commons}/udf/builtin/UDTFCos.java | 2 +- .../iotdb/commons}/udf/builtin/UDTFCosh.java | 2 +- .../commons}/udf/builtin/UDTFDegrees.java | 2 +- .../commons}/udf/builtin/UDTFDerivative.java | 14 +-- .../builtin/UDTFEqualSizeBucketAggSample.java | 20 ++-- .../builtin/UDTFEqualSizeBucketM4Sample.java | 20 ++-- .../UDTFEqualSizeBucketOutlierSample.java | 22 ++-- .../UDTFEqualSizeBucketRandomSample.java | 16 +-- .../builtin/UDTFEqualSizeBucketSample.java | 8 +- .../iotdb/commons}/udf/builtin/UDTFExp.java | 2 +- .../iotdb/commons}/udf/builtin/UDTFFloor.java | 2 +- .../commons}/udf/builtin/UDTFInRange.java | 20 ++-- .../iotdb/commons}/udf/builtin/UDTFJexl.java | 49 ++++----- .../iotdb/commons}/udf/builtin/UDTFLog.java | 2 +- .../iotdb/commons}/udf/builtin/UDTFLog10.java | 2 +- .../commons}/udf/builtin/UDTFMatches.java | 20 ++-- .../iotdb/commons}/udf/builtin/UDTFMath.java | 20 ++-- .../builtin/UDTFNonNegativeDerivative.java | 8 +- .../UDTFNonNegativeValueDifference.java | 8 +- .../udf/builtin/UDTFNonZeroCount.java | 2 +- .../udf/builtin/UDTFNonZeroDuration.java | 2 +- .../iotdb/commons}/udf/builtin/UDTFOnOff.java | 18 ++-- .../commons}/udf/builtin/UDTFRadians.java | 2 +- .../iotdb/commons}/udf/builtin/UDTFRound.java | 2 +- .../commons}/udf/builtin/UDTFSelectK.java | 23 ++-- .../iotdb/commons}/udf/builtin/UDTFSign.java | 2 +- .../iotdb/commons}/udf/builtin/UDTFSin.java | 2 +- .../iotdb/commons}/udf/builtin/UDTFSinh.java | 2 +- .../iotdb/commons}/udf/builtin/UDTFSqrt.java | 2 +- .../iotdb/commons}/udf/builtin/UDTFTan.java | 2 +- .../iotdb/commons}/udf/builtin/UDTFTanh.java | 2 +- .../udf/builtin/UDTFTimeDifference.java | 16 +-- .../iotdb/commons}/udf/builtin/UDTFTopK.java | 4 +- .../udf/builtin/UDTFValueDifference.java | 14 +-- .../commons}/udf/builtin/UDTFValueTrend.java | 14 +-- .../commons}/udf/builtin/UDTFZeroCount.java | 2 +- .../udf/builtin/UDTFZeroDuration.java | 2 +- .../selectinto/InsertTabletPlansIterator.java | 2 +- .../operator/process/FilterOperator.java | 4 +- .../operator/process/TransformOperator.java | 16 +-- .../iotdb/db/mpp/plan/analyze/Analysis.java | 2 +- .../iotdb/db/mpp/plan/analyze/Analyzer.java | 2 +- .../mpp/plan/analyze/ConcatPathRewriter.java | 2 +- .../mpp/plan/analyze/ExpressionAnalyzer.java | 38 +++---- .../db/mpp/plan/analyze/ExpressionUtils.java | 50 ++++----- .../plan/analyze/GroupByLevelController.java | 6 +- .../plan}/expression/Expression.java | 60 +++++------ .../plan}/expression/ExpressionType.java | 2 +- .../plan}/expression/ResultColumn.java | 2 +- .../expression/binary/AdditionExpression.java | 12 +-- .../binary/ArithmeticBinaryExpression.java | 4 +- .../expression/binary/BinaryExpression.java | 28 ++--- .../binary/CompareBinaryExpression.java | 4 +- .../expression/binary/DivisionExpression.java | 12 +-- .../expression/binary/EqualToExpression.java | 12 +-- .../binary/GreaterEqualExpression.java | 12 +-- .../binary/GreaterThanExpression.java | 12 +-- .../binary/LessEqualExpression.java | 12 +-- .../expression/binary/LessThanExpression.java | 12 +-- .../expression/binary/LogicAndExpression.java | 12 +-- .../binary/LogicBinaryExpression.java | 4 +- .../expression/binary/LogicOrExpression.java | 12 +-- .../expression/binary/ModuloExpression.java | 12 +-- .../binary/MultiplicationExpression.java | 12 +-- .../expression/binary/NonEqualExpression.java | 12 +-- .../binary/SubtractionExpression.java | 12 +-- .../expression/leaf/ConstantOperand.java | 20 ++-- .../plan}/expression/leaf/LeafOperand.java | 6 +- .../expression/leaf/TimeSeriesOperand.java | 24 ++--- .../expression/leaf/TimestampOperand.java | 24 ++--- .../expression/multi/FunctionExpression.java | 76 ++++++++----- .../plan}/expression/unary/InExpression.java | 12 +-- .../expression/unary/LikeExpression.java | 14 +-- .../expression/unary/LogicNotExpression.java | 20 ++-- .../expression/unary/NegationExpression.java | 20 ++-- .../expression/unary/RegularExpression.java | 12 +-- .../expression/unary/UnaryExpression.java | 26 ++--- .../iotdb/db/mpp/plan/parser/ASTVisitor.java | 46 ++++---- .../mpp/plan/parser/StatementGenerator.java | 12 +-- .../plan/planner/LocalExecutionPlanner.java | 2 +- .../mpp/plan/planner/LogicalPlanBuilder.java | 6 +- .../db/mpp/plan/planner/LogicalPlanner.java | 2 +- .../planner/plan/node/process/FilterNode.java | 2 +- .../plan/node/process/FilterNullNode.java | 2 +- .../plan/node/process/TransformNode.java | 2 +- .../plan/parameter/AggregationDescriptor.java | 2 +- .../plan/parameter/FilterNullParameter.java | 2 +- .../component/FilterNullComponent.java | 2 +- .../statement/component/ResultColumn.java | 2 +- .../statement/component/SelectComponent.java | 2 +- .../statement/component/WhereCondition.java | 2 +- .../plan/statement/crud/QueryStatement.java | 4 +- .../transformation/api}/LayerPointReader.java | 2 +- .../transformation/api}/LayerRowReader.java | 4 +- .../api}/LayerRowWindowReader.java | 4 +- ...ableRowRecordListBackedMultiColumnRow.java | 4 +- ...eRowRecordListBackedMultiColumnWindow.java | 10 +- ...rdListBackedMultiColumnWindowIterator.java | 8 +- ...rializableTVListBackedSingleColumnRow.java | 6 +- ...lizableTVListBackedSingleColumnWindow.java | 10 +- ...VListBackedSingleColumnWindowIterator.java | 8 +- ...LayerPointReaderBackedSingleColumnRow.java | 6 +- .../dag/builder}/DAGBuilder.java | 15 +-- .../dag/builder}/EvaluationDAGBuilder.java | 15 +-- .../dag/input/ConstantInputReader.java} | 9 +- .../dag/input/QueryDataSetInputLayer.java} | 21 ++-- .../dag/input}/TsBlockInputDataSet.java | 2 +- .../ConstantIntermediateLayer.java | 18 ++-- .../dag/intermediate}/IntermediateLayer.java | 16 +-- .../MultiInputColumnIntermediateLayer.java | 26 ++--- ...ColumnMultiReferenceIntermediateLayer.java | 31 +++--- ...olumnSingleReferenceIntermediateLayer.java | 28 ++--- .../dag/memory}/LayerMemoryAssigner.java | 6 +- .../dag/memory}/SafetyLine.java | 4 +- .../dag}/transformer/Transformer.java | 4 +- .../binary/ArithmeticAdditionTransformer.java | 4 +- .../binary/ArithmeticBinaryTransformer.java | 4 +- .../binary/ArithmeticDivisionTransformer.java | 4 +- .../binary/ArithmeticModuloTransformer.java | 4 +- .../ArithmeticMultiplicationTransformer.java | 4 +- .../ArithmeticSubtractionTransformer.java | 4 +- .../transformer/binary/BinaryTransformer.java | 6 +- .../binary/CompareBinaryTransformer.java | 4 +- .../binary/CompareEqualToTransformer.java | 4 +- .../CompareGreaterEqualTransformer.java | 4 +- .../binary/CompareGreaterThanTransformer.java | 4 +- .../binary/CompareLessEqualTransformer.java | 4 +- .../binary/CompareLessThanTransformer.java | 4 +- .../binary/CompareNonEqualTransformer.java | 4 +- .../binary/LogicAndTransformer.java | 5 +- .../binary/LogicBinaryTransformer.java | 4 +- .../binary/LogicOrTransformer.java | 4 +- .../multi/UDFQueryRowTransformer.java | 6 +- .../multi/UDFQueryRowWindowTransformer.java | 6 +- .../multi/UDFQueryTransformer.java | 8 +- .../unary/ArithmeticNegationTransformer.java | 4 +- .../dag}/transformer/unary/InTransformer.java | 4 +- .../unary/LogicNotTransformer.java | 4 +- .../transformer/unary/RegularTransformer.java | 4 +- .../unary/TransparentTransformer.java | 4 +- .../transformer/unary/UnaryTransformer.java | 6 +- .../transformation/dag/udf}/UDTFContext.java | 8 +- .../transformation/dag/udf}/UDTFExecutor.java | 82 +++++--------- .../dag/udf}/UDTFTypeInferrer.java | 33 +++--- .../dag/util}/InputRowUtils.java | 2 +- .../dag/util}/LayerCacheUtils.java | 8 +- .../transformation}/datastructure/Cache.java | 2 +- .../datastructure/SerializableList.java | 4 +- .../row/ElasticSerializableRowRecordList.java | 19 ++-- .../row/SerializableRowRecordList.java | 4 +- .../tv/ElasticSerializableBinaryTVList.java | 28 +++-- .../tv/ElasticSerializableTVList.java | 27 +++-- .../tv/SerializableBinaryTVList.java | 2 +- .../tv/SerializableBooleanTVList.java | 2 +- .../tv/SerializableDoubleTVList.java | 2 +- .../tv/SerializableFloatTVList.java | 2 +- .../tv/SerializableIntTVList.java | 2 +- .../tv/SerializableLongTVList.java | 2 +- .../datastructure/tv/SerializableTVList.java | 10 +- .../influxdb/function/InfluxFunction.java | 4 +- .../function/InfluxFunctionFactory.java | 2 +- .../function/aggregator/InfluxAggregator.java | 2 +- .../aggregator/InfluxCountFunction.java | 2 +- .../aggregator/InfluxMeanFunction.java | 2 +- .../aggregator/InfluxMedianFunction.java | 2 +- .../aggregator/InfluxModeFunction.java | 2 +- .../aggregator/InfluxSpreadFunction.java | 2 +- .../aggregator/InfluxStddevFunction.java | 2 +- .../aggregator/InfluxSumFunction.java | 2 +- .../selector/InfluxFirstFunction.java | 2 +- .../function/selector/InfluxLastFunction.java | 2 +- .../function/selector/InfluxMaxFunction.java | 2 +- .../function/selector/InfluxMinFunction.java | 2 +- .../function/selector/InfluxSelector.java | 2 +- .../influxdb/handler/QueryHandler.java | 8 +- .../operator/InfluxSelectComponent.java | 8 +- .../influxdb/sql/InfluxDBSqlVisitor.java | 20 ++-- .../rest/handler/QueryDataSetHandler.java | 2 +- .../iotdb/db/qp/constant/FilterConstant.java | 2 +- .../crud/AggregationQueryOperator.java | 8 +- .../db/qp/logical/crud/LastQueryOperator.java | 6 +- .../db/qp/logical/crud/QueryOperator.java | 8 +- .../db/qp/logical/crud/SelectComponent.java | 8 +- .../logical/crud/SpecialClauseComponent.java | 2 +- .../db/qp/logical/crud/UDAFQueryOperator.java | 8 +- .../db/qp/physical/crud/AggregationPlan.java | 2 +- .../qp/physical/crud/AlignByDevicePlan.java | 2 +- .../db/qp/physical/crud/LastQueryPlan.java | 2 +- .../iotdb/db/qp/physical/crud/QueryPlan.java | 2 +- .../db/qp/physical/crud/RawDataQueryPlan.java | 2 +- .../iotdb/db/qp/physical/crud/UDAFPlan.java | 2 +- .../iotdb/db/qp/physical/crud/UDFPlan.java | 2 +- .../iotdb/db/qp/physical/crud/UDTFPlan.java | 6 +- .../iotdb/db/qp/sql/IoTDBSqlVisitor.java | 50 ++++----- .../db/qp/strategy/LogicalGenerator.java | 4 +- .../optimizer/ConcatPathOptimizer.java | 6 +- .../iotdb/db/qp/utils/DatetimeUtils.java | 12 +-- .../db/qp/utils/GroupByLevelController.java | 6 +- .../iotdb/db/qp/utils/WildcardsRemover.java | 4 +- .../query/control/QueryResourceManager.java | 4 +- .../query/dataset/UDTFAlignByTimeDataSet.java | 6 +- .../iotdb/db/query/dataset/UDTFDataSet.java | 21 ++-- .../db/query/dataset/UDTFNonAlignDataSet.java | 4 +- .../dataset/groupby/GroupByFillDataSet.java | 2 +- .../udf/service/UDFClassLoaderManager.java | 3 +- .../service/UDFRegistrationInformation.java | 2 +- .../udf/service/UDFRegistrationService.java | 14 ++- .../org/apache/iotdb/db/service/DataNode.java | 1 - .../org/apache/iotdb/db/service/IoTDB.java | 1 - .../TemporaryQueryDataFileService.java | 4 +- .../db/tools/watermark/WatermarkDetector.java | 9 +- .../mpp/plan/plan/QueryLogicalPlanUtil.java | 14 +-- .../process/AggregationNodeSerdeTest.java | 2 +- .../node/process/FilterNodeSerdeTest.java | 8 +- .../node/process/FilterNullNodeSerdeTest.java | 2 +- .../process/GroupByLevelNodeSerdeTest.java | 2 +- .../SeriesAggregationScanNodeSerdeTest.java | 2 +- .../sql/InfluxDBLogicalGeneratorTest.java | 4 +- .../ElasticSerializableRowRecordListTest.java | 3 +- .../ElasticSerializableTVListTest.java | 17 ++- .../db/query/udf/datastructure/LRUCache.java | 2 + .../SerializableBinaryTVListTest.java | 4 +- .../SerializableBooleanTVListTest.java | 4 +- .../SerializableDoubleTVListTest.java | 4 +- .../SerializableFloatTVListTest.java | 4 +- .../SerializableIntTVListTest.java | 4 +- .../datastructure/SerializableListTest.java | 2 +- .../SerializableLongTVListTest.java | 4 +- .../SerializableRowRecordListTest.java | 2 +- .../iotdb/db/query/udf/example/Adder.java | 14 +-- 356 files changed, 1771 insertions(+), 1835 deletions(-) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/api/UDF.java (82%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/api/UDTF.java (86%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/api/access/Row.java (98%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/api/access/RowIterator.java (96%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/api/access/RowWindow.java (93%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/api/collector/PointCollector.java (88%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/api/customizer/config/UDFConfigurations.java (81%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/api/customizer/config/UDTFConfigurations.java (76%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/api/customizer/parameter/UDFParameterValidator.java (93%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/api/customizer/parameter/UDFParameters.java (70%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/api/customizer/strategy/AccessStrategy.java (76%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/api/customizer/strategy/RowByRowAccessStrategy.java (83%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/api/customizer/strategy/SlidingSizeWindowAccessStrategy.java (87%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/api/customizer/strategy/SlidingTimeWindowAccessStrategy.java (71%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/api/exception/UDFAttributeNotProvidedException.java (95%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/api/exception/UDFException.java (95%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/api/exception/UDFInputSeriesDataTypeNotValidException.java (96%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/api/exception/UDFInputSeriesIndexNotValidException.java (95%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/api/exception/UDFInputSeriesNumberNotValidException.java (96%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/api/exception/UDFOutputSeriesDataTypeNotValidException.java (95%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/api/exception/UDFParameterNotValidException.java (94%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/BuiltinFunction.java (98%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFAbs.java (81%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFAcos.java (95%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFAsin.java (95%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFAtan.java (95%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFBottomK.java (79%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFCast.java (84%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFCeil.java (95%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFCommonDerivative.java (89%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFCommonValueDifference.java (89%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFConst.java (85%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFConstE.java (73%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFConstPi.java (73%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFContains.java (72%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFContinuouslySatisfy.java (91%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFCos.java (95%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFCosh.java (95%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFDegrees.java (95%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFDerivative.java (77%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFEqualSizeBucketAggSample.java (95%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFEqualSizeBucketM4Sample.java (92%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFEqualSizeBucketOutlierSample.java (97%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFEqualSizeBucketRandomSample.java (78%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFEqualSizeBucketSample.java (87%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFExp.java (95%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFFloor.java (95%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFInRange.java (81%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFJexl.java (86%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFLog.java (95%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFLog10.java (95%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFMatches.java (73%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFMath.java (79%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFNonNegativeDerivative.java (89%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFNonNegativeValueDifference.java (89%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFNonZeroCount.java (97%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFNonZeroDuration.java (97%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFOnOff.java (80%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFRadians.java (95%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFRound.java (95%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFSelectK.java (87%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFSign.java (95%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFSin.java (95%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFSinh.java (95%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFSqrt.java (95%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFTan.java (95%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFTanh.java (95%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFTimeDifference.java (77%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFTopK.java (96%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFValueDifference.java (75%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFValueTrend.java (83%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFZeroCount.java (97%) rename {server/src/main/java/org/apache/iotdb/db/query => node-commons/src/main/java/org/apache/iotdb/commons}/udf/builtin/UDTFZeroDuration.java (97%) rename server/src/main/java/org/apache/iotdb/db/{query => mpp/plan}/expression/Expression.java (85%) rename server/src/main/java/org/apache/iotdb/db/{query => mpp/plan}/expression/ExpressionType.java (97%) rename server/src/main/java/org/apache/iotdb/db/{query => mpp/plan}/expression/ResultColumn.java (99%) rename server/src/main/java/org/apache/iotdb/db/{query => mpp/plan}/expression/binary/AdditionExpression.java (78%) rename server/src/main/java/org/apache/iotdb/db/{query => mpp/plan}/expression/binary/ArithmeticBinaryExpression.java (94%) rename server/src/main/java/org/apache/iotdb/db/{query => mpp/plan}/expression/binary/BinaryExpression.java (92%) rename server/src/main/java/org/apache/iotdb/db/{query => mpp/plan}/expression/binary/CompareBinaryExpression.java (96%) rename server/src/main/java/org/apache/iotdb/db/{query => mpp/plan}/expression/binary/DivisionExpression.java (78%) rename server/src/main/java/org/apache/iotdb/db/{query => mpp/plan}/expression/binary/EqualToExpression.java (78%) rename server/src/main/java/org/apache/iotdb/db/{query => mpp/plan}/expression/binary/GreaterEqualExpression.java (78%) rename server/src/main/java/org/apache/iotdb/db/{query => mpp/plan}/expression/binary/GreaterThanExpression.java (78%) rename server/src/main/java/org/apache/iotdb/db/{query => mpp/plan}/expression/binary/LessEqualExpression.java (78%) rename server/src/main/java/org/apache/iotdb/db/{query => mpp/plan}/expression/binary/LessThanExpression.java (78%) rename server/src/main/java/org/apache/iotdb/db/{query => mpp/plan}/expression/binary/LogicAndExpression.java (78%) rename server/src/main/java/org/apache/iotdb/db/{query => mpp/plan}/expression/binary/LogicBinaryExpression.java (94%) rename server/src/main/java/org/apache/iotdb/db/{query => mpp/plan}/expression/binary/LogicOrExpression.java (78%) rename server/src/main/java/org/apache/iotdb/db/{query => mpp/plan}/expression/binary/ModuloExpression.java (77%) rename server/src/main/java/org/apache/iotdb/db/{query => mpp/plan}/expression/binary/MultiplicationExpression.java (78%) rename server/src/main/java/org/apache/iotdb/db/{query => mpp/plan}/expression/binary/NonEqualExpression.java (78%) rename server/src/main/java/org/apache/iotdb/db/{query => mpp/plan}/expression/binary/SubtractionExpression.java (78%) rename server/src/main/java/org/apache/iotdb/db/{query => mpp/plan}/expression/leaf/ConstantOperand.java (87%) rename server/src/main/java/org/apache/iotdb/db/{query => mpp/plan}/expression/leaf/LeafOperand.java (87%) rename server/src/main/java/org/apache/iotdb/db/{query => mpp/plan}/expression/leaf/TimeSeriesOperand.java (86%) rename server/src/main/java/org/apache/iotdb/db/{query => mpp/plan}/expression/leaf/TimestampOperand.java (85%) rename server/src/main/java/org/apache/iotdb/db/{query => mpp/plan}/expression/multi/FunctionExpression.java (87%) rename server/src/main/java/org/apache/iotdb/db/{query => mpp/plan}/expression/unary/InExpression.java (89%) rename server/src/main/java/org/apache/iotdb/db/{query => mpp/plan}/expression/unary/LikeExpression.java (91%) rename server/src/main/java/org/apache/iotdb/db/{query => mpp/plan}/expression/unary/LogicNotExpression.java (77%) rename server/src/main/java/org/apache/iotdb/db/{query => mpp/plan}/expression/unary/NegationExpression.java (79%) rename server/src/main/java/org/apache/iotdb/db/{query => mpp/plan}/expression/unary/RegularExpression.java (88%) rename server/src/main/java/org/apache/iotdb/db/{query => mpp/plan}/expression/unary/UnaryExpression.java (89%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core/reader => mpp/transformation/api}/LayerPointReader.java (96%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core/reader => mpp/transformation/api}/LayerRowReader.java (92%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core/reader => mpp/transformation/api}/LayerRowWindowReader.java (91%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core/access => mpp/transformation/dag/adapter}/ElasticSerializableRowRecordListBackedMultiColumnRow.java (95%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core/access => mpp/transformation/dag/adapter}/ElasticSerializableRowRecordListBackedMultiColumnWindow.java (89%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core/access => mpp/transformation/dag/adapter}/ElasticSerializableRowRecordListBackedMultiColumnWindowIterator.java (87%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core/access => mpp/transformation/dag/adapter}/ElasticSerializableTVListBackedSingleColumnRow.java (92%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core/access => mpp/transformation/dag/adapter}/ElasticSerializableTVListBackedSingleColumnWindow.java (88%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core/access => mpp/transformation/dag/adapter}/ElasticSerializableTVListBackedSingleColumnWindowIterator.java (86%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core/access => mpp/transformation/dag/adapter}/LayerPointReaderBackedSingleColumnRow.java (93%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core/layer => mpp/transformation/dag/builder}/DAGBuilder.java (85%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core/layer => mpp/transformation/dag/builder}/EvaluationDAGBuilder.java (86%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core/reader/ConstantLayerPointReader.java => mpp/transformation/dag/input/ConstantInputReader.java} (90%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core/layer/RawQueryInputLayer.java => mpp/transformation/dag/input/QueryDataSetInputLayer.java} (93%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core/layer => mpp/transformation/dag/input}/TsBlockInputDataSet.java (97%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core/layer => mpp/transformation/dag/intermediate}/ConstantIntermediateLayer.java (75%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core/layer => mpp/transformation/dag/intermediate}/IntermediateLayer.java (81%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core/layer => mpp/transformation/dag/intermediate}/MultiInputColumnIntermediateLayer.java (91%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core/layer => mpp/transformation/dag/intermediate}/SingleInputColumnMultiReferenceIntermediateLayer.java (90%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core/layer => mpp/transformation/dag/intermediate}/SingleInputColumnSingleReferenceIntermediateLayer.java (89%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core/layer => mpp/transformation/dag/memory}/LayerMemoryAssigner.java (91%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core/layer => mpp/transformation/dag/memory}/SafetyLine.java (94%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core => mpp/transformation/dag}/transformer/Transformer.java (94%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core => mpp/transformation/dag}/transformer/binary/ArithmeticAdditionTransformer.java (89%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core => mpp/transformation/dag}/transformer/binary/ArithmeticBinaryTransformer.java (94%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core => mpp/transformation/dag}/transformer/binary/ArithmeticDivisionTransformer.java (89%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core => mpp/transformation/dag}/transformer/binary/ArithmeticModuloTransformer.java (89%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core => mpp/transformation/dag}/transformer/binary/ArithmeticMultiplicationTransformer.java (89%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core => mpp/transformation/dag}/transformer/binary/ArithmeticSubtractionTransformer.java (89%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core => mpp/transformation/dag}/transformer/binary/BinaryTransformer.java (95%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core => mpp/transformation/dag}/transformer/binary/CompareBinaryTransformer.java (95%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core => mpp/transformation/dag}/transformer/binary/CompareEqualToTransformer.java (92%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core => mpp/transformation/dag}/transformer/binary/CompareGreaterEqualTransformer.java (92%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core => mpp/transformation/dag}/transformer/binary/CompareGreaterThanTransformer.java (92%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core => mpp/transformation/dag}/transformer/binary/CompareLessEqualTransformer.java (92%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core => mpp/transformation/dag}/transformer/binary/CompareLessThanTransformer.java (92%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core => mpp/transformation/dag}/transformer/binary/CompareNonEqualTransformer.java (92%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core => mpp/transformation/dag}/transformer/binary/LogicAndTransformer.java (88%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core => mpp/transformation/dag}/transformer/binary/LogicBinaryTransformer.java (93%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core => mpp/transformation/dag}/transformer/binary/LogicOrTransformer.java (89%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core => mpp/transformation/dag}/transformer/multi/UDFQueryRowTransformer.java (88%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core => mpp/transformation/dag}/transformer/multi/UDFQueryRowWindowTransformer.java (87%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core => mpp/transformation/dag}/transformer/multi/UDFQueryTransformer.java (92%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core => mpp/transformation/dag}/transformer/unary/ArithmeticNegationTransformer.java (93%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core => mpp/transformation/dag}/transformer/unary/InTransformer.java (97%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core => mpp/transformation/dag}/transformer/unary/LogicNotTransformer.java (92%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core => mpp/transformation/dag}/transformer/unary/RegularTransformer.java (93%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core => mpp/transformation/dag}/transformer/unary/TransparentTransformer.java (94%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core => mpp/transformation/dag}/transformer/unary/UnaryTransformer.java (90%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core/executor => mpp/transformation/dag/udf}/UDTFContext.java (89%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core/executor => mpp/transformation/dag/udf}/UDTFExecutor.java (54%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core/executor => mpp/transformation/dag/udf}/UDTFTypeInferrer.java (67%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core/layer => mpp/transformation/dag/util}/InputRowUtils.java (97%) rename server/src/main/java/org/apache/iotdb/db/{query/udf/core/layer => mpp/transformation/dag/util}/LayerCacheUtils.java (91%) rename server/src/main/java/org/apache/iotdb/db/{query/udf => mpp/transformation}/datastructure/Cache.java (97%) rename server/src/main/java/org/apache/iotdb/db/{query/udf => mpp/transformation}/datastructure/SerializableList.java (97%) rename server/src/main/java/org/apache/iotdb/db/{query/udf => mpp/transformation}/datastructure/row/ElasticSerializableRowRecordList.java (93%) rename server/src/main/java/org/apache/iotdb/db/{query/udf => mpp/transformation}/datastructure/row/SerializableRowRecordList.java (98%) rename server/src/main/java/org/apache/iotdb/db/{query/udf => mpp/transformation}/datastructure/tv/ElasticSerializableBinaryTVList.java (83%) rename server/src/main/java/org/apache/iotdb/db/{query/udf => mpp/transformation}/datastructure/tv/ElasticSerializableTVList.java (93%) rename server/src/main/java/org/apache/iotdb/db/{query/udf => mpp/transformation}/datastructure/tv/SerializableBinaryTVList.java (98%) rename server/src/main/java/org/apache/iotdb/db/{query/udf => mpp/transformation}/datastructure/tv/SerializableBooleanTVList.java (97%) rename server/src/main/java/org/apache/iotdb/db/{query/udf => mpp/transformation}/datastructure/tv/SerializableDoubleTVList.java (97%) rename server/src/main/java/org/apache/iotdb/db/{query/udf => mpp/transformation}/datastructure/tv/SerializableFloatTVList.java (97%) rename server/src/main/java/org/apache/iotdb/db/{query/udf => mpp/transformation}/datastructure/tv/SerializableIntTVList.java (97%) rename server/src/main/java/org/apache/iotdb/db/{query/udf => mpp/transformation}/datastructure/tv/SerializableLongTVList.java (97%) rename server/src/main/java/org/apache/iotdb/db/{query/udf => mpp/transformation}/datastructure/tv/SerializableTVList.java (90%) rename server/src/main/java/org/apache/iotdb/db/{query/udf => }/service/TemporaryQueryDataFileService.java (96%) diff --git a/cluster/src/test/java/org/apache/iotdb/cluster/query/ClusterPhysicalGeneratorTest.java b/cluster/src/test/java/org/apache/iotdb/cluster/query/ClusterPhysicalGeneratorTest.java index d0585de36b82..c53f8e2eaf8b 100644 --- a/cluster/src/test/java/org/apache/iotdb/cluster/query/ClusterPhysicalGeneratorTest.java +++ b/cluster/src/test/java/org/apache/iotdb/cluster/query/ClusterPhysicalGeneratorTest.java @@ -23,12 +23,12 @@ import org.apache.iotdb.commons.exception.IllegalPathException; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.exception.query.QueryProcessException; +import org.apache.iotdb.db.mpp.plan.expression.ResultColumn; +import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand; import org.apache.iotdb.db.qp.logical.crud.FromComponent; import org.apache.iotdb.db.qp.logical.crud.QueryOperator; import org.apache.iotdb.db.qp.logical.crud.SelectComponent; import org.apache.iotdb.db.qp.physical.crud.RawDataQueryPlan; -import org.apache.iotdb.db.query.expression.ResultColumn; -import org.apache.iotdb.db.query.expression.leaf.TimeSeriesOperand; import org.junit.Before; import org.junit.Test; diff --git a/cluster/src/test/java/org/apache/iotdb/cluster/query/last/ClusterLastQueryExecutorTest.java b/cluster/src/test/java/org/apache/iotdb/cluster/query/last/ClusterLastQueryExecutorTest.java index 0cbd269cfa77..39d54bf0a80e 100644 --- a/cluster/src/test/java/org/apache/iotdb/cluster/query/last/ClusterLastQueryExecutorTest.java +++ b/cluster/src/test/java/org/apache/iotdb/cluster/query/last/ClusterLastQueryExecutorTest.java @@ -26,10 +26,10 @@ import org.apache.iotdb.db.exception.StorageEngineException; import org.apache.iotdb.db.exception.query.QueryProcessException; import org.apache.iotdb.db.metadata.path.MeasurementPath; +import org.apache.iotdb.db.mpp.plan.expression.ResultColumn; import org.apache.iotdb.db.qp.physical.crud.LastQueryPlan; import org.apache.iotdb.db.query.context.QueryContext; import org.apache.iotdb.db.query.control.QueryResourceManager; -import org.apache.iotdb.db.query.expression.ResultColumn; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.read.expression.IExpression; import org.apache.iotdb.tsfile.read.expression.impl.GlobalTimeExpression; diff --git a/example/udf/src/main/java/org/apache/iotdb/udf/UDTFExample.java b/example/udf/src/main/java/org/apache/iotdb/udf/UDTFExample.java index 66a07482d5c6..b5882d72fd15 100644 --- a/example/udf/src/main/java/org/apache/iotdb/udf/UDTFExample.java +++ b/example/udf/src/main/java/org/apache/iotdb/udf/UDTFExample.java @@ -19,13 +19,13 @@ package org.apache.iotdb.udf; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.io.IOException; diff --git a/integration/src/main/java/org/apache/iotdb/db/query/udf/example/Accumulator.java b/integration/src/main/java/org/apache/iotdb/db/query/udf/example/Accumulator.java index cb6387f2ebda..e7393f85c960 100644 --- a/integration/src/main/java/org/apache/iotdb/db/query/udf/example/Accumulator.java +++ b/integration/src/main/java/org/apache/iotdb/db/query/udf/example/Accumulator.java @@ -19,17 +19,17 @@ package org.apache.iotdb.db.query.udf.example; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.access.RowIterator; -import org.apache.iotdb.db.query.udf.api.access.RowWindow; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingTimeWindowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.access.RowIterator; +import org.apache.iotdb.commons.udf.api.access.RowWindow; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingTimeWindowAccessStrategy; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.slf4j.Logger; diff --git a/integration/src/main/java/org/apache/iotdb/db/query/udf/example/Adder.java b/integration/src/main/java/org/apache/iotdb/db/query/udf/example/Adder.java index 2b1fc1f13dc1..8b9ea955f48f 100644 --- a/integration/src/main/java/org/apache/iotdb/db/query/udf/example/Adder.java +++ b/integration/src/main/java/org/apache/iotdb/db/query/udf/example/Adder.java @@ -19,13 +19,13 @@ package org.apache.iotdb.db.query.udf.example; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.tsfile.exception.write.UnSupportedDataTypeException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/integration/src/main/java/org/apache/iotdb/db/query/udf/example/Counter.java b/integration/src/main/java/org/apache/iotdb/db/query/udf/example/Counter.java index 6ca4deaac656..b6b8955e9fa1 100644 --- a/integration/src/main/java/org/apache/iotdb/db/query/udf/example/Counter.java +++ b/integration/src/main/java/org/apache/iotdb/db/query/udf/example/Counter.java @@ -19,15 +19,15 @@ package org.apache.iotdb.db.query.udf.example; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.access.RowWindow; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingTimeWindowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.access.RowWindow; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingTimeWindowAccessStrategy; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.slf4j.Logger; diff --git a/integration/src/main/java/org/apache/iotdb/db/query/udf/example/Max.java b/integration/src/main/java/org/apache/iotdb/db/query/udf/example/Max.java index ef4aa50c6a63..42795487c0e2 100644 --- a/integration/src/main/java/org/apache/iotdb/db/query/udf/example/Max.java +++ b/integration/src/main/java/org/apache/iotdb/db/query/udf/example/Max.java @@ -19,13 +19,13 @@ package org.apache.iotdb.db.query.udf.example; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.slf4j.Logger; diff --git a/integration/src/main/java/org/apache/iotdb/db/query/udf/example/Multiplier.java b/integration/src/main/java/org/apache/iotdb/db/query/udf/example/Multiplier.java index 9b901263816a..8055ab143544 100644 --- a/integration/src/main/java/org/apache/iotdb/db/query/udf/example/Multiplier.java +++ b/integration/src/main/java/org/apache/iotdb/db/query/udf/example/Multiplier.java @@ -19,13 +19,13 @@ package org.apache.iotdb.db.query.udf.example; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.slf4j.Logger; diff --git a/integration/src/main/java/org/apache/iotdb/db/query/udf/example/SlidingSizeWindowConstructorTester0.java b/integration/src/main/java/org/apache/iotdb/db/query/udf/example/SlidingSizeWindowConstructorTester0.java index 436eaf9cb2cd..2ead453dd417 100644 --- a/integration/src/main/java/org/apache/iotdb/db/query/udf/example/SlidingSizeWindowConstructorTester0.java +++ b/integration/src/main/java/org/apache/iotdb/db/query/udf/example/SlidingSizeWindowConstructorTester0.java @@ -19,12 +19,12 @@ package org.apache.iotdb.db.query.udf.example; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.RowWindow; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.RowWindow; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.slf4j.Logger; diff --git a/integration/src/main/java/org/apache/iotdb/db/query/udf/example/SlidingSizeWindowConstructorTester1.java b/integration/src/main/java/org/apache/iotdb/db/query/udf/example/SlidingSizeWindowConstructorTester1.java index fc132333c7f3..ed6050c2adb2 100644 --- a/integration/src/main/java/org/apache/iotdb/db/query/udf/example/SlidingSizeWindowConstructorTester1.java +++ b/integration/src/main/java/org/apache/iotdb/db/query/udf/example/SlidingSizeWindowConstructorTester1.java @@ -19,13 +19,13 @@ package org.apache.iotdb.db.query.udf.example; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.slf4j.Logger; diff --git a/integration/src/main/java/org/apache/iotdb/db/query/udf/example/SlidingTimeWindowConstructionTester.java b/integration/src/main/java/org/apache/iotdb/db/query/udf/example/SlidingTimeWindowConstructionTester.java index 917811ff34d0..111aad3c7567 100644 --- a/integration/src/main/java/org/apache/iotdb/db/query/udf/example/SlidingTimeWindowConstructionTester.java +++ b/integration/src/main/java/org/apache/iotdb/db/query/udf/example/SlidingTimeWindowConstructionTester.java @@ -19,14 +19,14 @@ package org.apache.iotdb.db.query.udf.example; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.RowIterator; -import org.apache.iotdb.db.query.udf.api.access.RowWindow; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingTimeWindowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.RowIterator; +import org.apache.iotdb.commons.udf.api.access.RowWindow; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingTimeWindowAccessStrategy; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.slf4j.Logger; diff --git a/integration/src/main/java/org/apache/iotdb/db/query/udf/example/TerminateTester.java b/integration/src/main/java/org/apache/iotdb/db/query/udf/example/TerminateTester.java index a7fba82c728f..909984940ce3 100644 --- a/integration/src/main/java/org/apache/iotdb/db/query/udf/example/TerminateTester.java +++ b/integration/src/main/java/org/apache/iotdb/db/query/udf/example/TerminateTester.java @@ -19,12 +19,12 @@ package org.apache.iotdb.db.query.udf.example; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.slf4j.Logger; diff --git a/integration/src/main/java/org/apache/iotdb/db/query/udf/example/ValidateTester.java b/integration/src/main/java/org/apache/iotdb/db/query/udf/example/ValidateTester.java index b5c0dea54f76..7f53d9fe5942 100644 --- a/integration/src/main/java/org/apache/iotdb/db/query/udf/example/ValidateTester.java +++ b/integration/src/main/java/org/apache/iotdb/db/query/udf/example/ValidateTester.java @@ -19,11 +19,11 @@ package org.apache.iotdb.db.query.udf.example; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; public class ValidateTester implements UDTF { diff --git a/integration/src/main/java/org/apache/iotdb/db/query/udf/example/WindowStartEnd.java b/integration/src/main/java/org/apache/iotdb/db/query/udf/example/WindowStartEnd.java index 2745becbdf87..ccf002aea0f9 100644 --- a/integration/src/main/java/org/apache/iotdb/db/query/udf/example/WindowStartEnd.java +++ b/integration/src/main/java/org/apache/iotdb/db/query/udf/example/WindowStartEnd.java @@ -19,13 +19,13 @@ package org.apache.iotdb.db.query.udf.example; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.RowWindow; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingTimeWindowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.RowWindow; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingTimeWindowAccessStrategy; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.io.IOException; diff --git a/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBSyntaxConventionStringLiteralIT.java b/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBSyntaxConventionStringLiteralIT.java index de3eba8a91d9..957c9674530c 100644 --- a/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBSyntaxConventionStringLiteralIT.java +++ b/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBSyntaxConventionStringLiteralIT.java @@ -18,7 +18,7 @@ */ package org.apache.iotdb.db.integration; -import org.apache.iotdb.db.query.udf.builtin.BuiltinFunction; +import org.apache.iotdb.commons.udf.builtin.BuiltinFunction; import org.apache.iotdb.integration.env.EnvFactory; import org.apache.iotdb.itbase.category.ClusterTest; import org.apache.iotdb.itbase.category.LocalStandaloneTest; diff --git a/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBUDFManagementIT.java b/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBUDFManagementIT.java index 4ad46aa1f583..84c1920805b7 100644 --- a/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBUDFManagementIT.java +++ b/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBUDFManagementIT.java @@ -20,8 +20,8 @@ package org.apache.iotdb.db.integration; import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.commons.udf.builtin.BuiltinFunction; import org.apache.iotdb.db.qp.constant.SQLConstant; -import org.apache.iotdb.db.query.udf.builtin.BuiltinFunction; import org.apache.iotdb.db.query.udf.service.UDFRegistrationService; import org.apache.iotdb.db.service.IoTDB; import org.apache.iotdb.db.utils.EnvironmentUtils; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFIQR.java b/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFIQR.java index 5f358200ada6..4646fe103da9 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFIQR.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFIQR.java @@ -19,13 +19,13 @@ package org.apache.iotdb.library.anomaly; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.library.util.Util; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFKSigma.java b/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFKSigma.java index 4c734115efbe..1ddd01ad4d4e 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFKSigma.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFKSigma.java @@ -19,13 +19,13 @@ package org.apache.iotdb.library.anomaly; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.library.util.CircularQueue; import org.apache.iotdb.library.util.LongCircularQueue; import org.apache.iotdb.library.util.Util; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFLOF.java b/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFLOF.java index 5c5d8d229475..e747b2a06637 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFLOF.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFLOF.java @@ -19,13 +19,13 @@ package org.apache.iotdb.library.anomaly; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.RowWindow; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.RowWindow; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; import org.apache.iotdb.library.util.Util; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFMissDetect.java b/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFMissDetect.java index a146799b1944..29bc70c9d4c8 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFMissDetect.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFMissDetect.java @@ -19,13 +19,13 @@ package org.apache.iotdb.library.anomaly; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.library.anomaly.util.StreamMissDetector; import org.apache.iotdb.library.util.Util; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFRange.java b/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFRange.java index c0fec326962c..26a9af205f74 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFRange.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFRange.java @@ -19,13 +19,13 @@ package org.apache.iotdb.library.anomaly; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.library.util.Util; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFTwoSidedFilter.java b/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFTwoSidedFilter.java index 80a4f545d5ac..f8f72a5437cf 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFTwoSidedFilter.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/anomaly/UDTFTwoSidedFilter.java @@ -19,13 +19,13 @@ package org.apache.iotdb.library.anomaly; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.RowWindow; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.RowWindow; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; import org.apache.iotdb.library.anomaly.util.WindowDetect; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/anomaly/util/MissDetector.java b/library-udf/src/main/java/org/apache/iotdb/library/anomaly/util/MissDetector.java index 18a2db5cf223..8d654ace8acd 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/anomaly/util/MissDetector.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/anomaly/util/MissDetector.java @@ -19,8 +19,8 @@ package org.apache.iotdb.library.anomaly.util; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.access.RowIterator; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.access.RowIterator; import org.apache.iotdb.library.util.Util; import org.apache.commons.math3.stat.regression.SimpleRegression; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/anomaly/util/WindowDetect.java b/library-udf/src/main/java/org/apache/iotdb/library/anomaly/util/WindowDetect.java index 0d8e15d23ecd..9828d61acb16 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/anomaly/util/WindowDetect.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/anomaly/util/WindowDetect.java @@ -19,8 +19,8 @@ package org.apache.iotdb.library.anomaly.util; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.access.RowIterator; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.access.RowIterator; import org.apache.iotdb.library.util.Util; import java.util.ArrayList; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/dmatch/UDAFCov.java b/library-udf/src/main/java/org/apache/iotdb/library/dmatch/UDAFCov.java index bf61929ac361..403f51d6d186 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/dmatch/UDAFCov.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/dmatch/UDAFCov.java @@ -19,13 +19,13 @@ package org.apache.iotdb.library.dmatch; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.library.util.Util; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/dmatch/UDAFDtw.java b/library-udf/src/main/java/org/apache/iotdb/library/dmatch/UDAFDtw.java index 90fae2816e9e..fd9c408f8a65 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/dmatch/UDAFDtw.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/dmatch/UDAFDtw.java @@ -19,14 +19,14 @@ package org.apache.iotdb.library.dmatch; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.access.RowWindow; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.access.RowWindow; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; import org.apache.iotdb.library.util.Util; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/dmatch/UDAFPearson.java b/library-udf/src/main/java/org/apache/iotdb/library/dmatch/UDAFPearson.java index 3a8e97ac1a45..56a1a6dcf073 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/dmatch/UDAFPearson.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/dmatch/UDAFPearson.java @@ -19,13 +19,13 @@ package org.apache.iotdb.library.dmatch; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.library.util.Util; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/dmatch/UDTFPtnSym.java b/library-udf/src/main/java/org/apache/iotdb/library/dmatch/UDTFPtnSym.java index 98478e751bab..f95d46e4a997 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/dmatch/UDTFPtnSym.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/dmatch/UDTFPtnSym.java @@ -19,14 +19,14 @@ package org.apache.iotdb.library.dmatch; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.access.RowWindow; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.access.RowWindow; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; import org.apache.iotdb.library.util.Util; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/dmatch/UDTFXCorr.java b/library-udf/src/main/java/org/apache/iotdb/library/dmatch/UDTFXCorr.java index b72a3a276d11..4f08709be8c5 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/dmatch/UDTFXCorr.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/dmatch/UDTFXCorr.java @@ -19,13 +19,13 @@ package org.apache.iotdb.library.dmatch; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.library.dmatch.util.CrossCorrelation; import org.apache.iotdb.library.util.Util; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFIntegral.java b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFIntegral.java index 87fa45a63a8e..0299557484fe 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFIntegral.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFIntegral.java @@ -19,13 +19,13 @@ package org.apache.iotdb.library.dprofile; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.library.util.Util; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFIntegralAvg.java b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFIntegralAvg.java index 2250ca98161e..486ec29b383c 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFIntegralAvg.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFIntegralAvg.java @@ -19,13 +19,13 @@ package org.apache.iotdb.library.dprofile; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.library.util.Util; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFMad.java b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFMad.java index 9164198c35a3..602b6f6cdc24 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFMad.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFMad.java @@ -19,13 +19,13 @@ package org.apache.iotdb.library.dprofile; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.library.dprofile.util.ExactOrderStatistics; import org.apache.iotdb.library.dprofile.util.MADSketch; import org.apache.iotdb.library.util.Util; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFMedian.java b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFMedian.java index 00b45af92cdb..8c61703245f9 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFMedian.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFMedian.java @@ -19,13 +19,13 @@ package org.apache.iotdb.library.dprofile; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.library.dprofile.util.ExactOrderStatistics; import org.apache.iotdb.library.dprofile.util.GKArray; import org.apache.iotdb.library.util.Util; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFMode.java b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFMode.java index e19cce850a7b..002c52484b07 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFMode.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFMode.java @@ -18,13 +18,13 @@ */ package org.apache.iotdb.library.dprofile; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.library.dprofile.util.MaxSelector; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFPercentile.java b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFPercentile.java index 6eebfee98859..100cd8010922 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFPercentile.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFPercentile.java @@ -19,13 +19,13 @@ package org.apache.iotdb.library.dprofile; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.library.dprofile.util.ExactOrderStatistics; import org.apache.iotdb.library.dprofile.util.GKArray; import org.apache.iotdb.library.util.Util; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFPeriod.java b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFPeriod.java index 892e553f79d9..c8f6469be0af 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFPeriod.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFPeriod.java @@ -19,15 +19,15 @@ package org.apache.iotdb.library.dprofile; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.access.RowIterator; -import org.apache.iotdb.db.query.udf.api.access.RowWindow; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.access.RowIterator; +import org.apache.iotdb.commons.udf.api.access.RowWindow; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; import org.apache.iotdb.library.util.Util; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFSkew.java b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFSkew.java index 559f1cc857f9..4d3c82c734a2 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFSkew.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFSkew.java @@ -19,13 +19,13 @@ package org.apache.iotdb.library.dprofile; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.library.util.Util; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFSpread.java b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFSpread.java index 414ff37c0a23..7a237fa93838 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFSpread.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFSpread.java @@ -19,13 +19,13 @@ package org.apache.iotdb.library.dprofile; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.library.util.NoNumberException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFStddev.java b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFStddev.java index 5611c0116c3b..b01598cdf3a9 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFStddev.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDAFStddev.java @@ -19,13 +19,13 @@ package org.apache.iotdb.library.dprofile; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.library.util.Util; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFACF.java b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFACF.java index 3bc410c816a0..8ab3337f84c1 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFACF.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFACF.java @@ -19,13 +19,13 @@ package org.apache.iotdb.library.dprofile; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.library.dprofile.util.CrossCorrelation; import org.apache.iotdb.library.util.Util; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFDistinct.java b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFDistinct.java index 5f0ab6c89da1..d1b552ef16d1 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFDistinct.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFDistinct.java @@ -19,13 +19,13 @@ package org.apache.iotdb.library.dprofile; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.eclipse.collections.api.iterator.MutableBooleanIterator; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFHistogram.java b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFHistogram.java index 4f0c4ce52923..a24dcc197b5b 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFHistogram.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFHistogram.java @@ -18,13 +18,13 @@ */ package org.apache.iotdb.library.dprofile; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.library.util.Util; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFMinMax.java b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFMinMax.java index a7812c0c4a9a..ee5e0fb9c6a4 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFMinMax.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFMinMax.java @@ -19,13 +19,13 @@ package org.apache.iotdb.library.dprofile; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.library.util.Util; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFMvAvg.java b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFMvAvg.java index 4226354db889..b5aea28fa11a 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFMvAvg.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFMvAvg.java @@ -19,13 +19,13 @@ package org.apache.iotdb.library.dprofile; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.library.util.DoubleCircularQueue; import org.apache.iotdb.library.util.Util; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFPACF.java b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFPACF.java index e9622ee2fbec..7c73e535111e 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFPACF.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFPACF.java @@ -18,13 +18,13 @@ */ package org.apache.iotdb.library.dprofile; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.library.dprofile.util.YuleWalker; import org.apache.iotdb.library.util.Util; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFQLB.java b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFQLB.java index f213ef824b55..6f014c894af6 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFQLB.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFQLB.java @@ -18,13 +18,13 @@ */ package org.apache.iotdb.library.dprofile; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.library.util.Util; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFResample.java b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFResample.java index 229dbef8c6da..220b08f24747 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFResample.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFResample.java @@ -19,13 +19,13 @@ package org.apache.iotdb.library.dprofile; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.library.dprofile.util.Resampler; import org.apache.iotdb.library.util.Util; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFSample.java b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFSample.java index 370e244143a4..5df57ecf9122 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFSample.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFSample.java @@ -19,16 +19,16 @@ package org.apache.iotdb.library.dprofile; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.access.RowIterator; -import org.apache.iotdb.db.query.udf.api.access.RowWindow; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.access.RowIterator; +import org.apache.iotdb.commons.udf.api.access.RowWindow; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; import org.apache.iotdb.library.util.Util; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFSegment.java b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFSegment.java index dbdfc0801197..0c56942d00fc 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFSegment.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFSegment.java @@ -18,13 +18,13 @@ */ package org.apache.iotdb.library.dprofile; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.library.dprofile.util.Segment; import org.apache.iotdb.library.util.Util; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFSpline.java b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFSpline.java index cf6925e5db25..13d3ef5fb751 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFSpline.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFSpline.java @@ -19,13 +19,13 @@ package org.apache.iotdb.library.dprofile; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.library.util.Util; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFZScore.java b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFZScore.java index 1f1a8ead608c..4b1c5f0ceb49 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFZScore.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/UDTFZScore.java @@ -19,13 +19,13 @@ package org.apache.iotdb.library.dprofile; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.library.util.Util; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/util/ExactOrderStatistics.java b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/util/ExactOrderStatistics.java index e6aa0e9d3a0d..61d5aa24e82d 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/dprofile/util/ExactOrderStatistics.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/dprofile/util/ExactOrderStatistics.java @@ -18,8 +18,8 @@ */ package org.apache.iotdb.library.dprofile.util; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.exception.UDFInputSeriesDataTypeNotValidException; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.exception.UDFInputSeriesDataTypeNotValidException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.eclipse.collections.impl.list.mutable.primitive.DoubleArrayList; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/dquality/UDTFCompleteness.java b/library-udf/src/main/java/org/apache/iotdb/library/dquality/UDTFCompleteness.java index db1d35663277..d4d8b55ece20 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/dquality/UDTFCompleteness.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/dquality/UDTFCompleteness.java @@ -19,13 +19,13 @@ package org.apache.iotdb.library.dquality; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.RowWindow; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingTimeWindowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.RowWindow; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingTimeWindowAccessStrategy; import org.apache.iotdb.library.dquality.util.TimeSeriesQuality; import org.apache.iotdb.library.util.Util; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/dquality/UDTFConsistency.java b/library-udf/src/main/java/org/apache/iotdb/library/dquality/UDTFConsistency.java index b01a7f35a330..5b131d4a1f96 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/dquality/UDTFConsistency.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/dquality/UDTFConsistency.java @@ -19,13 +19,13 @@ package org.apache.iotdb.library.dquality; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.RowWindow; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingTimeWindowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.RowWindow; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingTimeWindowAccessStrategy; import org.apache.iotdb.library.dquality.util.TimeSeriesQuality; import org.apache.iotdb.library.util.NoNumberException; import org.apache.iotdb.library.util.Util; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/dquality/UDTFTimeliness.java b/library-udf/src/main/java/org/apache/iotdb/library/dquality/UDTFTimeliness.java index 89552f4b16e8..26203c026c73 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/dquality/UDTFTimeliness.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/dquality/UDTFTimeliness.java @@ -19,13 +19,13 @@ package org.apache.iotdb.library.dquality; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.RowWindow; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingTimeWindowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.RowWindow; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingTimeWindowAccessStrategy; import org.apache.iotdb.library.dquality.util.TimeSeriesQuality; import org.apache.iotdb.library.util.NoNumberException; import org.apache.iotdb.library.util.Util; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/dquality/UDTFValidity.java b/library-udf/src/main/java/org/apache/iotdb/library/dquality/UDTFValidity.java index 99e3345c7239..2f66d491affc 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/dquality/UDTFValidity.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/dquality/UDTFValidity.java @@ -19,13 +19,13 @@ package org.apache.iotdb.library.dquality; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.RowWindow; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingTimeWindowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.RowWindow; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingTimeWindowAccessStrategy; import org.apache.iotdb.library.dquality.util.TimeSeriesQuality; import org.apache.iotdb.library.util.NoNumberException; import org.apache.iotdb.library.util.Util; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/dquality/util/TimeSeriesQuality.java b/library-udf/src/main/java/org/apache/iotdb/library/dquality/util/TimeSeriesQuality.java index 2734413d661e..39e5a82d0ef8 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/dquality/util/TimeSeriesQuality.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/dquality/util/TimeSeriesQuality.java @@ -19,8 +19,8 @@ package org.apache.iotdb.library.dquality.util; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.access.RowIterator; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.access.RowIterator; import org.apache.iotdb.library.util.Util; import org.apache.commons.math3.stat.descriptive.rank.Median; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/drepair/UDTFTimestampRepair.java b/library-udf/src/main/java/org/apache/iotdb/library/drepair/UDTFTimestampRepair.java index 85bcbc3bb06c..143d77d5755f 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/drepair/UDTFTimestampRepair.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/drepair/UDTFTimestampRepair.java @@ -19,13 +19,13 @@ package org.apache.iotdb.library.drepair; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.RowWindow; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.RowWindow; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; import org.apache.iotdb.library.drepair.util.TimestampRepair; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/drepair/UDTFValueFill.java b/library-udf/src/main/java/org/apache/iotdb/library/drepair/UDTFValueFill.java index 9d148b573b01..a43d581fe186 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/drepair/UDTFValueFill.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/drepair/UDTFValueFill.java @@ -18,13 +18,13 @@ */ package org.apache.iotdb.library.drepair; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.RowWindow; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.RowWindow; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; import org.apache.iotdb.library.drepair.util.ARFill; import org.apache.iotdb.library.drepair.util.LikelihoodFill; import org.apache.iotdb.library.drepair.util.LinearFill; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/drepair/UDTFValueRepair.java b/library-udf/src/main/java/org/apache/iotdb/library/drepair/UDTFValueRepair.java index a7cd8ca7747b..9207d8e4b8d1 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/drepair/UDTFValueRepair.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/drepair/UDTFValueRepair.java @@ -19,13 +19,13 @@ package org.apache.iotdb.library.drepair; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.RowWindow; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.RowWindow; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; import org.apache.iotdb.library.drepair.util.LsGreedy; import org.apache.iotdb.library.drepair.util.Screen; import org.apache.iotdb.library.drepair.util.ValueRepair; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/ARFill.java b/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/ARFill.java index cc362cbfe476..44a3f77c6264 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/ARFill.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/ARFill.java @@ -18,8 +18,8 @@ */ package org.apache.iotdb.library.drepair.util; -import org.apache.iotdb.db.query.udf.api.access.RowIterator; -import org.apache.iotdb.db.query.udf.api.exception.UDFException; +import org.apache.iotdb.commons.udf.api.access.RowIterator; +import org.apache.iotdb.commons.udf.api.exception.UDFException; public class ARFill extends ValueFill { // TODO Higer order AR regression diff --git a/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/LikelihoodFill.java b/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/LikelihoodFill.java index cc26be1a24dc..5e035cf3dfa7 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/LikelihoodFill.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/LikelihoodFill.java @@ -18,7 +18,7 @@ */ package org.apache.iotdb.library.drepair.util; -import org.apache.iotdb.db.query.udf.api.access.RowIterator; +import org.apache.iotdb.commons.udf.api.access.RowIterator; import java.util.ArrayList; import java.util.List; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/LinearFill.java b/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/LinearFill.java index 8c8a41b29b1d..635b6f6b11d3 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/LinearFill.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/LinearFill.java @@ -18,7 +18,7 @@ */ package org.apache.iotdb.library.drepair.util; -import org.apache.iotdb.db.query.udf.api.access.RowIterator; +import org.apache.iotdb.commons.udf.api.access.RowIterator; public class LinearFill extends ValueFill { diff --git a/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/LsGreedy.java b/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/LsGreedy.java index 21decefc2036..52ea1023c0b8 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/LsGreedy.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/LsGreedy.java @@ -18,7 +18,7 @@ */ package org.apache.iotdb.library.drepair.util; -import org.apache.iotdb.db.query.udf.api.access.RowIterator; +import org.apache.iotdb.commons.udf.api.access.RowIterator; import org.apache.iotdb.library.util.Util; import java.util.PriorityQueue; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/MAFill.java b/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/MAFill.java index c1e90d538211..4488b4ad4f63 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/MAFill.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/MAFill.java @@ -18,7 +18,7 @@ */ package org.apache.iotdb.library.drepair.util; -import org.apache.iotdb.db.query.udf.api.access.RowIterator; +import org.apache.iotdb.commons.udf.api.access.RowIterator; public class MAFill extends ValueFill { int window_size = 5; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/MeanFill.java b/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/MeanFill.java index f7d8d6377b2e..7db2f2fd786e 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/MeanFill.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/MeanFill.java @@ -18,7 +18,7 @@ */ package org.apache.iotdb.library.drepair.util; -import org.apache.iotdb.db.query.udf.api.access.RowIterator; +import org.apache.iotdb.commons.udf.api.access.RowIterator; public class MeanFill extends ValueFill { public MeanFill(RowIterator dataIterator) throws Exception { diff --git a/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/PreviousFill.java b/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/PreviousFill.java index a424d50b992e..a3a58d546b7a 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/PreviousFill.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/PreviousFill.java @@ -18,7 +18,7 @@ */ package org.apache.iotdb.library.drepair.util; -import org.apache.iotdb.db.query.udf.api.access.RowIterator; +import org.apache.iotdb.commons.udf.api.access.RowIterator; public class PreviousFill extends ValueFill { diff --git a/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/Screen.java b/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/Screen.java index 8c3877478aca..7eb09c60939a 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/Screen.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/Screen.java @@ -18,7 +18,7 @@ */ package org.apache.iotdb.library.drepair.util; -import org.apache.iotdb.db.query.udf.api.access.RowIterator; +import org.apache.iotdb.commons.udf.api.access.RowIterator; import org.apache.iotdb.library.util.Util; import org.apache.commons.lang3.tuple.Pair; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/ScreenFill.java b/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/ScreenFill.java index 40131bc9e66c..4c96e1ee768f 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/ScreenFill.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/ScreenFill.java @@ -18,8 +18,8 @@ */ package org.apache.iotdb.library.drepair.util; -import org.apache.iotdb.db.query.udf.api.access.RowIterator; -import org.apache.iotdb.db.query.udf.api.exception.UDFException; +import org.apache.iotdb.commons.udf.api.access.RowIterator; +import org.apache.iotdb.commons.udf.api.exception.UDFException; import org.apache.iotdb.library.util.Util; import org.apache.commons.lang3.tuple.Pair; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/TimestampRepair.java b/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/TimestampRepair.java index b01e87a56fe2..196e35a64fc6 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/TimestampRepair.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/TimestampRepair.java @@ -18,8 +18,8 @@ */ package org.apache.iotdb.library.drepair.util; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.access.RowIterator; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.access.RowIterator; import org.apache.iotdb.library.util.Util; import org.slf4j.Logger; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/ValueFill.java b/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/ValueFill.java index 1275f960db97..3d1d9c7b0e83 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/ValueFill.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/ValueFill.java @@ -18,9 +18,9 @@ */ package org.apache.iotdb.library.drepair.util; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.access.RowIterator; -import org.apache.iotdb.db.query.udf.api.exception.UDFException; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.access.RowIterator; +import org.apache.iotdb.commons.udf.api.exception.UDFException; import org.apache.iotdb.library.util.Util; import java.util.ArrayList; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/ValueRepair.java b/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/ValueRepair.java index 0d8f53256aca..49c0056ad9c1 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/ValueRepair.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/drepair/util/ValueRepair.java @@ -18,8 +18,8 @@ */ package org.apache.iotdb.library.drepair.util; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.access.RowIterator; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.access.RowIterator; import org.apache.iotdb.library.util.Util; import java.util.ArrayList; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/frequency/UDTFConv.java b/library-udf/src/main/java/org/apache/iotdb/library/frequency/UDTFConv.java index e756f794fb14..bddc15639a9d 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/frequency/UDTFConv.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/frequency/UDTFConv.java @@ -19,13 +19,13 @@ package org.apache.iotdb.library.frequency; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.library.util.Util; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/frequency/UDTFDWT.java b/library-udf/src/main/java/org/apache/iotdb/library/frequency/UDTFDWT.java index 615de54b4c6b..069a369b7520 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/frequency/UDTFDWT.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/frequency/UDTFDWT.java @@ -18,13 +18,13 @@ */ package org.apache.iotdb.library.frequency; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.library.frequency.util.DWTUtil; import org.apache.iotdb.library.util.Util; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/frequency/UDTFDeconv.java b/library-udf/src/main/java/org/apache/iotdb/library/frequency/UDTFDeconv.java index 7f6e743486fd..a44521271bed 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/frequency/UDTFDeconv.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/frequency/UDTFDeconv.java @@ -19,13 +19,13 @@ package org.apache.iotdb.library.frequency; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.library.util.Util; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/frequency/UDTFFFT.java b/library-udf/src/main/java/org/apache/iotdb/library/frequency/UDTFFFT.java index 2aae08c477e1..6dfece67cfe5 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/frequency/UDTFFFT.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/frequency/UDTFFFT.java @@ -19,13 +19,13 @@ package org.apache.iotdb.library.frequency; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.library.frequency.util.FFTUtil; import org.apache.iotdb.library.util.Util; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/frequency/UDTFHighPass.java b/library-udf/src/main/java/org/apache/iotdb/library/frequency/UDTFHighPass.java index 070f1930417f..0a0eb49c840a 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/frequency/UDTFHighPass.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/frequency/UDTFHighPass.java @@ -18,13 +18,13 @@ */ package org.apache.iotdb.library.frequency; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.library.util.Util; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/frequency/UDTFIDWT.java b/library-udf/src/main/java/org/apache/iotdb/library/frequency/UDTFIDWT.java index 38d75b24a486..b086499dd0b9 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/frequency/UDTFIDWT.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/frequency/UDTFIDWT.java @@ -18,13 +18,13 @@ */ package org.apache.iotdb.library.frequency; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.library.frequency.util.DWTUtil; import org.apache.iotdb.library.util.Util; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/frequency/UDTFIFFT.java b/library-udf/src/main/java/org/apache/iotdb/library/frequency/UDTFIFFT.java index bf2ddb95faa0..408654d50381 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/frequency/UDTFIFFT.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/frequency/UDTFIFFT.java @@ -19,13 +19,13 @@ package org.apache.iotdb.library.frequency; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.library.util.Util; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/frequency/UDTFLowPass.java b/library-udf/src/main/java/org/apache/iotdb/library/frequency/UDTFLowPass.java index 855d17041e58..7260dc993514 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/frequency/UDTFLowPass.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/frequency/UDTFLowPass.java @@ -18,13 +18,13 @@ */ package org.apache.iotdb.library.frequency; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.library.util.Util; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/frequency/util/FFTUtil.java b/library-udf/src/main/java/org/apache/iotdb/library/frequency/util/FFTUtil.java index 848a1587949a..574f770a1da5 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/frequency/util/FFTUtil.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/frequency/util/FFTUtil.java @@ -18,7 +18,7 @@ */ package org.apache.iotdb.library.frequency.util; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/series/UDTFConsecutiveSequences.java b/library-udf/src/main/java/org/apache/iotdb/library/series/UDTFConsecutiveSequences.java index 66d5abb24da0..9c043bd47a99 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/series/UDTFConsecutiveSequences.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/series/UDTFConsecutiveSequences.java @@ -16,13 +16,13 @@ package org.apache.iotdb.library.series; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.library.series.util.ConsecutiveUtil; import org.apache.iotdb.library.util.Util; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/series/UDTFConsecutiveWindows.java b/library-udf/src/main/java/org/apache/iotdb/library/series/UDTFConsecutiveWindows.java index 9424a7ac2692..9a2765fcf4f1 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/series/UDTFConsecutiveWindows.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/series/UDTFConsecutiveWindows.java @@ -16,13 +16,13 @@ package org.apache.iotdb.library.series; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.library.series.util.ConsecutiveUtil; import org.apache.iotdb.library.util.Util; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/series/util/ConsecutiveUtil.java b/library-udf/src/main/java/org/apache/iotdb/library/series/util/ConsecutiveUtil.java index a5e085b46f6c..35d8d0d9d55f 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/series/util/ConsecutiveUtil.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/series/util/ConsecutiveUtil.java @@ -15,8 +15,8 @@ */ package org.apache.iotdb.library.series.util; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; import org.apache.iotdb.library.util.Util; import org.apache.commons.lang3.tuple.Pair; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/string/UDTFRegexMatch.java b/library-udf/src/main/java/org/apache/iotdb/library/string/UDTFRegexMatch.java index cbe4ef62cfc1..5b4580a24f93 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/string/UDTFRegexMatch.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/string/UDTFRegexMatch.java @@ -19,13 +19,13 @@ package org.apache.iotdb.library.string; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.util.regex.Matcher; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/string/UDTFRegexReplace.java b/library-udf/src/main/java/org/apache/iotdb/library/string/UDTFRegexReplace.java index 2af3bbbc7c12..80891ae0f258 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/string/UDTFRegexReplace.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/string/UDTFRegexReplace.java @@ -19,13 +19,13 @@ package org.apache.iotdb.library.string; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.eclipse.collections.impl.list.mutable.primitive.IntArrayList; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/string/UDTFRegexSplit.java b/library-udf/src/main/java/org/apache/iotdb/library/string/UDTFRegexSplit.java index 45a9c79d1e1c..9f00641b18d7 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/string/UDTFRegexSplit.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/string/UDTFRegexSplit.java @@ -19,13 +19,13 @@ package org.apache.iotdb.library.string; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; /** This function splits string from an input series according to given regex. */ diff --git a/library-udf/src/main/java/org/apache/iotdb/library/string/UDTFStrReplace.java b/library-udf/src/main/java/org/apache/iotdb/library/string/UDTFStrReplace.java index b6b19f4bcc4b..1b67789c9617 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/string/UDTFStrReplace.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/string/UDTFStrReplace.java @@ -19,13 +19,13 @@ package org.apache.iotdb.library.string; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; /** This function does limited times of replacement of substring from an input series. */ diff --git a/library-udf/src/main/java/org/apache/iotdb/library/util/LinearRegression.java b/library-udf/src/main/java/org/apache/iotdb/library/util/LinearRegression.java index b79ac7f8c35f..00d9d4b160a8 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/util/LinearRegression.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/util/LinearRegression.java @@ -18,7 +18,7 @@ */ package org.apache.iotdb.library.util; -import org.apache.iotdb.db.query.udf.api.exception.UDFException; +import org.apache.iotdb.commons.udf.api.exception.UDFException; import java.util.Arrays; diff --git a/library-udf/src/main/java/org/apache/iotdb/library/util/Util.java b/library-udf/src/main/java/org/apache/iotdb/library/util/Util.java index f1a1896546d4..734005aa38ac 100644 --- a/library-udf/src/main/java/org/apache/iotdb/library/util/Util.java +++ b/library-udf/src/main/java/org/apache/iotdb/library/util/Util.java @@ -18,8 +18,8 @@ */ package org.apache.iotdb.library.util; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.commons.math3.stat.descriptive.rank.Median; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/api/UDF.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/UDF.java similarity index 82% rename from server/src/main/java/org/apache/iotdb/db/query/udf/api/UDF.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/api/UDF.java index ffa19132e71e..e7d37fdd02b9 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/api/UDF.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/UDF.java @@ -17,11 +17,11 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.api; +package org.apache.iotdb.commons.udf.api; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; public interface UDF { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/api/UDTF.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/UDTF.java similarity index 86% rename from server/src/main/java/org/apache/iotdb/db/query/udf/api/UDTF.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/api/UDTF.java index 7f29e1f1d1d1..48b8b1e91f5a 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/api/UDTF.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/UDTF.java @@ -17,17 +17,17 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.api; +package org.apache.iotdb.commons.udf.api; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.access.RowWindow; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingTimeWindowAccessStrategy; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.access.RowWindow; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingTimeWindowAccessStrategy; /** * User-defined Time-series Generating Function (UDTF) diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/api/access/Row.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/access/Row.java similarity index 98% rename from server/src/main/java/org/apache/iotdb/db/query/udf/api/access/Row.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/api/access/Row.java index eaf1f7c960f9..989a33c66d1c 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/api/access/Row.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/access/Row.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.api.access; +package org.apache.iotdb.commons.udf.api.access; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.utils.Binary; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/api/access/RowIterator.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/access/RowIterator.java similarity index 96% rename from server/src/main/java/org/apache/iotdb/db/query/udf/api/access/RowIterator.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/api/access/RowIterator.java index fa941dfcc9c3..fd8b1bec9986 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/api/access/RowIterator.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/access/RowIterator.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.api.access; +package org.apache.iotdb.commons.udf.api.access; import java.io.IOException; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/api/access/RowWindow.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/access/RowWindow.java similarity index 93% rename from server/src/main/java/org/apache/iotdb/db/query/udf/api/access/RowWindow.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/api/access/RowWindow.java index 54b6caf0ec8e..99d5ff855098 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/api/access/RowWindow.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/access/RowWindow.java @@ -17,10 +17,10 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.api.access; +package org.apache.iotdb.commons.udf.api.access; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingTimeWindowAccessStrategy; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingTimeWindowAccessStrategy; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.io.IOException; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/api/collector/PointCollector.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/collector/PointCollector.java similarity index 88% rename from server/src/main/java/org/apache/iotdb/db/query/udf/api/collector/PointCollector.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/api/collector/PointCollector.java index d1f4b9fafb4c..1867933902c5 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/api/collector/PointCollector.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/collector/PointCollector.java @@ -17,14 +17,13 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.api.collector; +package org.apache.iotdb.commons.udf.api.collector; -import org.apache.iotdb.db.exception.query.QueryProcessException; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.access.RowWindow; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.access.RowWindow; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.utils.Binary; @@ -116,10 +115,10 @@ public interface PointCollector { * @param timestamp timestamp to collect * @param value Binary value to collect * @throws IOException if any I/O errors occur - * @throws QueryProcessException if memory is not enough to continue collecting data points + * @throws RuntimeException if memory is not enough to continue collecting data points * @see TSDataType */ - void putBinary(long timestamp, Binary value) throws IOException, QueryProcessException; + void putBinary(long timestamp, Binary value) throws IOException; /** * Collects a String data point with timestamp. @@ -131,8 +130,8 @@ public interface PointCollector { * @param timestamp timestamp to collect * @param value String value to collect * @throws IOException if any I/O errors occur - * @throws QueryProcessException if memory is not enough to continue collecting data points + * @throws RuntimeException if memory is not enough to continue collecting data points * @see TSDataType */ - void putString(long timestamp, String value) throws IOException, QueryProcessException; + void putString(long timestamp, String value) throws IOException; } diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/api/customizer/config/UDFConfigurations.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/customizer/config/UDFConfigurations.java similarity index 81% rename from server/src/main/java/org/apache/iotdb/db/query/udf/api/customizer/config/UDFConfigurations.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/api/customizer/config/UDFConfigurations.java index 9b4c55f04a14..713b6e162e83 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/api/customizer/config/UDFConfigurations.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/customizer/config/UDFConfigurations.java @@ -17,9 +17,8 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.api.customizer.config; +package org.apache.iotdb.commons.udf.api.customizer.config; -import org.apache.iotdb.db.exception.query.QueryProcessException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; public abstract class UDFConfigurations { @@ -30,9 +29,9 @@ public TSDataType getOutputDataType() { return outputDataType; } - public void check() throws QueryProcessException { + public void check() { if (outputDataType == null) { - throw new QueryProcessException("UDF outputDataType is not set."); + throw new RuntimeException("UDF outputDataType is not set."); } } } diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/api/customizer/config/UDTFConfigurations.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/customizer/config/UDTFConfigurations.java similarity index 76% rename from server/src/main/java/org/apache/iotdb/db/query/udf/api/customizer/config/UDTFConfigurations.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/api/customizer/config/UDTFConfigurations.java index ab431d68563c..51985352cbb1 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/api/customizer/config/UDTFConfigurations.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/customizer/config/UDTFConfigurations.java @@ -17,18 +17,17 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.api.customizer.config; +package org.apache.iotdb.commons.udf.api.customizer.config; -import org.apache.iotdb.db.exception.query.QueryProcessException; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.access.RowWindow; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.AccessStrategy; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingTimeWindowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.access.RowWindow; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.AccessStrategy; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingTimeWindowAccessStrategy; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.time.ZoneId; @@ -96,10 +95,10 @@ public UDTFConfigurations setAccessStrategy(AccessStrategy accessStrategy) { } @Override - public void check() throws QueryProcessException { + public void check() { super.check(); if (accessStrategy == null) { - throw new QueryProcessException("Access strategy is not set."); + throw new RuntimeException("Access strategy is not set."); } accessStrategy.check(); } diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/api/customizer/parameter/UDFParameterValidator.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/customizer/parameter/UDFParameterValidator.java similarity index 93% rename from server/src/main/java/org/apache/iotdb/db/query/udf/api/customizer/parameter/UDFParameterValidator.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/api/customizer/parameter/UDFParameterValidator.java index abc983e93084..34f9255423d7 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/api/customizer/parameter/UDFParameterValidator.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/customizer/parameter/UDFParameterValidator.java @@ -17,15 +17,15 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.api.customizer.parameter; +package org.apache.iotdb.commons.udf.api.customizer.parameter; import org.apache.iotdb.commons.exception.MetadataException; -import org.apache.iotdb.db.query.udf.api.exception.UDFAttributeNotProvidedException; -import org.apache.iotdb.db.query.udf.api.exception.UDFException; -import org.apache.iotdb.db.query.udf.api.exception.UDFInputSeriesDataTypeNotValidException; -import org.apache.iotdb.db.query.udf.api.exception.UDFInputSeriesIndexNotValidException; -import org.apache.iotdb.db.query.udf.api.exception.UDFInputSeriesNumberNotValidException; -import org.apache.iotdb.db.query.udf.api.exception.UDFParameterNotValidException; +import org.apache.iotdb.commons.udf.api.exception.UDFAttributeNotProvidedException; +import org.apache.iotdb.commons.udf.api.exception.UDFException; +import org.apache.iotdb.commons.udf.api.exception.UDFInputSeriesDataTypeNotValidException; +import org.apache.iotdb.commons.udf.api.exception.UDFInputSeriesIndexNotValidException; +import org.apache.iotdb.commons.udf.api.exception.UDFInputSeriesNumberNotValidException; +import org.apache.iotdb.commons.udf.api.exception.UDFParameterNotValidException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; public class UDFParameterValidator { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/api/customizer/parameter/UDFParameters.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/customizer/parameter/UDFParameters.java similarity index 70% rename from server/src/main/java/org/apache/iotdb/db/query/udf/api/customizer/parameter/UDFParameters.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/api/customizer/parameter/UDFParameters.java index 9b9b6640f096..65de587a8848 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/api/customizer/parameter/UDFParameters.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/customizer/parameter/UDFParameters.java @@ -17,19 +17,14 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.api.customizer.parameter; +package org.apache.iotdb.commons.udf.api.customizer.parameter; import org.apache.iotdb.commons.exception.MetadataException; import org.apache.iotdb.commons.path.PartialPath; -import org.apache.iotdb.db.exception.query.QueryProcessException; -import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.multi.FunctionExpression; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; -import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -47,40 +42,28 @@ */ public class UDFParameters { - private final List expressions; - private final List paths; + private final List childExpressions; + private final List maybeTimeSeriesPaths; + private final List childExpressionDataTypes; private final Map attributes; - private final List dataTypes; public UDFParameters( - FunctionExpression functionExpression, Map expressionDataTypeMap) - throws QueryProcessException { - expressions = functionExpression.getExpressions(); - paths = functionExpression.getPaths(); - attributes = functionExpression.getFunctionAttributes(); - dataTypes = new ArrayList<>(); - for (Expression expression : expressions) { - dataTypes.add(expressionDataTypeMap.get(expression)); - } + List childExpressions, + List maybeTimeSeriesPaths, + List childExpressionDataTypes, + Map attributes) { + this.childExpressions = childExpressions; + this.maybeTimeSeriesPaths = maybeTimeSeriesPaths; + this.childExpressionDataTypes = childExpressionDataTypes; + this.attributes = attributes; } - public UDFParameters(FunctionExpression functionExpression, TypeProvider typeProvider) - throws QueryProcessException { - expressions = functionExpression.getExpressions(); - paths = functionExpression.getPaths(); - attributes = functionExpression.getFunctionAttributes(); - dataTypes = new ArrayList<>(); - for (Expression expression : expressions) { - dataTypes.add(typeProvider.getType(expression.toString())); - } - } - - public List getExpressions() { - return expressions; + public List getChildExpressions() { + return childExpressions; } public List getPaths() { - return paths; + return maybeTimeSeriesPaths; } public Map getAttributes() { @@ -88,15 +71,15 @@ public Map getAttributes() { } public List getDataTypes() throws MetadataException { - return dataTypes; + return childExpressionDataTypes; } public PartialPath getPath(int index) { - return paths.get(index); + return maybeTimeSeriesPaths.get(index); } public TSDataType getDataType(int index) throws MetadataException { - return dataTypes.get(index); + return childExpressionDataTypes.get(index); } public boolean hasAttribute(String attributeKey) { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/api/customizer/strategy/AccessStrategy.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/customizer/strategy/AccessStrategy.java similarity index 76% rename from server/src/main/java/org/apache/iotdb/db/query/udf/api/customizer/strategy/AccessStrategy.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/api/customizer/strategy/AccessStrategy.java index e825ad21796b..15ff92910b48 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/api/customizer/strategy/AccessStrategy.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/customizer/strategy/AccessStrategy.java @@ -17,12 +17,11 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.api.customizer.strategy; +package org.apache.iotdb.commons.udf.api.customizer.strategy; -import org.apache.iotdb.db.exception.query.QueryProcessException; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; /** * Used to customize the strategy for accessing raw data in {@link UDTF#beforeStart(UDFParameters, @@ -45,9 +44,9 @@ enum AccessStrategyType { /** * Used by the system to check the access strategy. * - * @throws QueryProcessException if invalid strategy is set + * @throws RuntimeException if invalid strategy is set */ - void check() throws QueryProcessException; + void check(); /** * Returns the actual access strategy type. diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/api/customizer/strategy/RowByRowAccessStrategy.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/customizer/strategy/RowByRowAccessStrategy.java similarity index 83% rename from server/src/main/java/org/apache/iotdb/db/query/udf/api/customizer/strategy/RowByRowAccessStrategy.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/api/customizer/strategy/RowByRowAccessStrategy.java index d44177237081..7880af552878 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/api/customizer/strategy/RowByRowAccessStrategy.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/customizer/strategy/RowByRowAccessStrategy.java @@ -17,13 +17,13 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.api.customizer.strategy; +package org.apache.iotdb.commons.udf.api.customizer.strategy; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; /** * Used in {@link UDTF#beforeStart(UDFParameters, UDTFConfigurations)}. diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/api/customizer/strategy/SlidingSizeWindowAccessStrategy.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/customizer/strategy/SlidingSizeWindowAccessStrategy.java similarity index 87% rename from server/src/main/java/org/apache/iotdb/db/query/udf/api/customizer/strategy/SlidingSizeWindowAccessStrategy.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/api/customizer/strategy/SlidingSizeWindowAccessStrategy.java index e7e7f101b724..c534a81fe5ad 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/api/customizer/strategy/SlidingSizeWindowAccessStrategy.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/customizer/strategy/SlidingSizeWindowAccessStrategy.java @@ -17,14 +17,13 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.api.customizer.strategy; +package org.apache.iotdb.commons.udf.api.customizer.strategy; -import org.apache.iotdb.db.exception.query.QueryProcessException; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.RowWindow; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.RowWindow; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; /** * Used in {@link UDTF#beforeStart(UDFParameters, UDTFConfigurations)}. @@ -98,13 +97,13 @@ public SlidingSizeWindowAccessStrategy(int windowSize) { } @Override - public void check() throws QueryProcessException { + public void check() { if (windowSize <= 0) { - throw new QueryProcessException( + throw new RuntimeException( String.format("Parameter windowSize(%d) should be positive.", windowSize)); } if (slidingStep <= 0) { - throw new QueryProcessException( + throw new RuntimeException( String.format("Parameter slidingStep(%d) should be positive.", slidingStep)); } } diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/api/customizer/strategy/SlidingTimeWindowAccessStrategy.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/customizer/strategy/SlidingTimeWindowAccessStrategy.java similarity index 71% rename from server/src/main/java/org/apache/iotdb/db/query/udf/api/customizer/strategy/SlidingTimeWindowAccessStrategy.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/api/customizer/strategy/SlidingTimeWindowAccessStrategy.java index 2d08b464ae0d..4eab05ca35c5 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/api/customizer/strategy/SlidingTimeWindowAccessStrategy.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/customizer/strategy/SlidingTimeWindowAccessStrategy.java @@ -17,15 +17,13 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.api.customizer.strategy; +package org.apache.iotdb.commons.udf.api.customizer.strategy; -import org.apache.iotdb.db.exception.query.QueryProcessException; -import org.apache.iotdb.db.qp.utils.DatetimeUtils; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.RowWindow; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.RowWindow; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; import java.time.ZoneId; @@ -62,7 +60,7 @@ * parameters.getLong(0), // display window begin * parameters.getLong(10000))); // display window end * } - *

Style 2: + *

Style 2 (deprecated since v0.14): *

{@code
  * @Override
  * public void beforeStart(UDFParameters parameters, UDTFConfigurations configurations) {
@@ -78,21 +76,16 @@
  */
 public class SlidingTimeWindowAccessStrategy implements AccessStrategy {
 
-  private final boolean inputInString;
-
-  private String timeIntervalString;
-  private String slidingStepString;
-  private String displayWindowBeginString;
-  private String displayWindowEndString;
-
-  private long timeInterval;
-  private long slidingStep;
-  private long displayWindowBegin;
-  private long displayWindowEnd;
+  private final long timeInterval;
+  private final long slidingStep;
+  private final long displayWindowBegin;
+  private final long displayWindowEnd;
 
   private ZoneId zoneId;
 
   /**
+   * Deprecated since v0.14.
+   *
    * @param timeIntervalString time interval in string. examples: 12d8m9ns, 1y1mo, etc. supported
    *     units: y, mo, w, d, h, m, s, ms, us, ns.
    * @param slidingStepString sliding step in string. examples: 12d8m9ns, 1y1mo, etc. supported
@@ -101,49 +94,48 @@ public class SlidingTimeWindowAccessStrategy implements AccessStrategy {
    *     2011-12-03T10:15:30+01:00.
    * @param displayWindowEndString display window end in string. format: 2011-12-03T10:15:30 or
    *     2011-12-03T10:15:30+01:00.
-   * @see DatetimeUtils.DurationUnit
+   * @throws UnsupportedOperationException deprecated since v0.14
    */
+  @Deprecated
   public SlidingTimeWindowAccessStrategy(
       String timeIntervalString,
       String slidingStepString,
       String displayWindowBeginString,
       String displayWindowEndString) {
-    inputInString = true;
-    this.timeIntervalString = timeIntervalString;
-    this.slidingStepString = slidingStepString;
-    this.displayWindowBeginString = displayWindowBeginString;
-    this.displayWindowEndString = displayWindowEndString;
+    throw new UnsupportedOperationException("The method is deprecated since v0.14.");
   }
 
   /**
-   * Display window begin will be set to the same as the minimum timestamp of the query result set,
-   * and display window end will be set to the same as the maximum timestamp of the query result
-   * set.
+   * Deprecated since v0.14.
+   *
+   * 

Display window begin will be set to the same as the minimum timestamp of the query result + * set, and display window end will be set to the same as the maximum timestamp of the query + * result set. * * @param timeIntervalString time interval in string. examples: 12d8m9ns, 1y1mo, etc. supported * units: y, mo, w, d, h, m, s, ms, us, ns. * @param slidingStepString sliding step in string. examples: 12d8m9ns, 1y1mo, etc. supported * units: y, mo, w, d, h, m, s, ms, us, ns. - * @see DatetimeUtils.DurationUnit + * @throws UnsupportedOperationException deprecated since v0.14 */ + @Deprecated public SlidingTimeWindowAccessStrategy(String timeIntervalString, String slidingStepString) { - inputInString = true; - this.timeIntervalString = timeIntervalString; - this.slidingStepString = slidingStepString; + throw new UnsupportedOperationException("The method is deprecated since v0.14."); } /** - * Sliding step will be set to the same as the time interval, display window begin will be set to - * the same as the minimum timestamp of the query result set, and display window end will be set - * to the same as the maximum timestamp of the query result set. + * Deprecated since v0.14. + * + *

Sliding step will be set to the same as the time interval, display window begin will be set + * to the same as the minimum timestamp of the query result set, and display window end will be + * set to the same as the maximum timestamp of the query result set. * * @param timeIntervalString time interval in string. examples: 12d8m9ns, 1y1mo, etc. supported * units: y, mo, w, d, h, m, s, ms, us, ns. - * @see DatetimeUtils.DurationUnit + * @throws UnsupportedOperationException deprecated since v0.14 */ public SlidingTimeWindowAccessStrategy(String timeIntervalString) { - inputInString = true; - this.timeIntervalString = timeIntervalString; + throw new UnsupportedOperationException("The method is deprecated since v0.14."); } /** @@ -154,7 +146,6 @@ public SlidingTimeWindowAccessStrategy(String timeIntervalString) { */ public SlidingTimeWindowAccessStrategy( long timeInterval, long slidingStep, long displayWindowBegin, long displayWindowEnd) { - inputInString = false; this.timeInterval = timeInterval; this.slidingStep = slidingStep; this.displayWindowBegin = displayWindowBegin; @@ -170,7 +161,6 @@ public SlidingTimeWindowAccessStrategy( * @param slidingStep 0 < slidingStep */ public SlidingTimeWindowAccessStrategy(long timeInterval, long slidingStep) { - inputInString = false; this.timeInterval = timeInterval; this.slidingStep = slidingStep; this.displayWindowBegin = Long.MIN_VALUE; @@ -185,7 +175,6 @@ public SlidingTimeWindowAccessStrategy(long timeInterval, long slidingStep) { * @param timeInterval 0 < timeInterval */ public SlidingTimeWindowAccessStrategy(long timeInterval) { - inputInString = false; this.timeInterval = timeInterval; this.slidingStep = timeInterval; this.displayWindowBegin = Long.MIN_VALUE; @@ -193,21 +182,17 @@ public SlidingTimeWindowAccessStrategy(long timeInterval) { } @Override - public void check() throws QueryProcessException { - if (inputInString) { - parseStringParameters(); - } - + public void check() { if (timeInterval <= 0) { - throw new QueryProcessException( + throw new RuntimeException( String.format("Parameter timeInterval(%d) should be positive.", timeInterval)); } if (slidingStep <= 0) { - throw new QueryProcessException( + throw new RuntimeException( String.format("Parameter slidingStep(%d) should be positive.", slidingStep)); } if (displayWindowEnd < displayWindowBegin) { - throw new QueryProcessException( + throw new RuntimeException( String.format( "displayWindowEnd(%d) < displayWindowBegin(%d)", displayWindowEnd, displayWindowBegin)); @@ -242,20 +227,4 @@ public void setZoneId(ZoneId zoneId) { public AccessStrategyType getAccessStrategyType() { return AccessStrategyType.SLIDING_TIME_WINDOW; } - - private void parseStringParameters() throws QueryProcessException { - timeInterval = DatetimeUtils.convertDurationStrToLong(timeIntervalString); - slidingStep = - slidingStepString == null - ? timeInterval - : DatetimeUtils.convertDurationStrToLong(slidingStepString); - displayWindowBegin = - displayWindowBeginString == null - ? Long.MIN_VALUE - : DatetimeUtils.convertDatetimeStrToLong(displayWindowBeginString, zoneId); - displayWindowEnd = - displayWindowEndString == null - ? Long.MAX_VALUE - : DatetimeUtils.convertDatetimeStrToLong(displayWindowEndString, zoneId); - } } diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/api/exception/UDFAttributeNotProvidedException.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/exception/UDFAttributeNotProvidedException.java similarity index 95% rename from server/src/main/java/org/apache/iotdb/db/query/udf/api/exception/UDFAttributeNotProvidedException.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/api/exception/UDFAttributeNotProvidedException.java index 0ff2a128fcd2..fe966149dde6 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/api/exception/UDFAttributeNotProvidedException.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/exception/UDFAttributeNotProvidedException.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.api.exception; +package org.apache.iotdb.commons.udf.api.exception; public class UDFAttributeNotProvidedException extends UDFParameterNotValidException { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/api/exception/UDFException.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/exception/UDFException.java similarity index 95% rename from server/src/main/java/org/apache/iotdb/db/query/udf/api/exception/UDFException.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/api/exception/UDFException.java index b2dcd90a0fe2..c4a974cd34c9 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/api/exception/UDFException.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/exception/UDFException.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.api.exception; +package org.apache.iotdb.commons.udf.api.exception; public class UDFException extends Exception { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/api/exception/UDFInputSeriesDataTypeNotValidException.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/exception/UDFInputSeriesDataTypeNotValidException.java similarity index 96% rename from server/src/main/java/org/apache/iotdb/db/query/udf/api/exception/UDFInputSeriesDataTypeNotValidException.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/api/exception/UDFInputSeriesDataTypeNotValidException.java index 7bcd9bcd9690..96df90aa6301 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/api/exception/UDFInputSeriesDataTypeNotValidException.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/exception/UDFInputSeriesDataTypeNotValidException.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.api.exception; +package org.apache.iotdb.commons.udf.api.exception; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/api/exception/UDFInputSeriesIndexNotValidException.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/exception/UDFInputSeriesIndexNotValidException.java similarity index 95% rename from server/src/main/java/org/apache/iotdb/db/query/udf/api/exception/UDFInputSeriesIndexNotValidException.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/api/exception/UDFInputSeriesIndexNotValidException.java index 7239fd76f99e..d5197298ed57 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/api/exception/UDFInputSeriesIndexNotValidException.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/exception/UDFInputSeriesIndexNotValidException.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.api.exception; +package org.apache.iotdb.commons.udf.api.exception; public class UDFInputSeriesIndexNotValidException extends UDFParameterNotValidException { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/api/exception/UDFInputSeriesNumberNotValidException.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/exception/UDFInputSeriesNumberNotValidException.java similarity index 96% rename from server/src/main/java/org/apache/iotdb/db/query/udf/api/exception/UDFInputSeriesNumberNotValidException.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/api/exception/UDFInputSeriesNumberNotValidException.java index bd899763aa9f..3ee4e75f4306 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/api/exception/UDFInputSeriesNumberNotValidException.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/exception/UDFInputSeriesNumberNotValidException.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.api.exception; +package org.apache.iotdb.commons.udf.api.exception; public class UDFInputSeriesNumberNotValidException extends UDFParameterNotValidException { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/api/exception/UDFOutputSeriesDataTypeNotValidException.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/exception/UDFOutputSeriesDataTypeNotValidException.java similarity index 95% rename from server/src/main/java/org/apache/iotdb/db/query/udf/api/exception/UDFOutputSeriesDataTypeNotValidException.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/api/exception/UDFOutputSeriesDataTypeNotValidException.java index f94591d97e9d..ff484594a928 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/api/exception/UDFOutputSeriesDataTypeNotValidException.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/exception/UDFOutputSeriesDataTypeNotValidException.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.api.exception; +package org.apache.iotdb.commons.udf.api.exception; public class UDFOutputSeriesDataTypeNotValidException extends UDFParameterNotValidException { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/api/exception/UDFParameterNotValidException.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/exception/UDFParameterNotValidException.java similarity index 94% rename from server/src/main/java/org/apache/iotdb/db/query/udf/api/exception/UDFParameterNotValidException.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/api/exception/UDFParameterNotValidException.java index 0951c354aa35..4fbfe2268bd2 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/api/exception/UDFParameterNotValidException.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/api/exception/UDFParameterNotValidException.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.api.exception; +package org.apache.iotdb.commons.udf.api.exception; public class UDFParameterNotValidException extends UDFException { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/BuiltinFunction.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/BuiltinFunction.java similarity index 98% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/BuiltinFunction.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/BuiltinFunction.java index bf1b7031e8c9..ed8bc85d3384 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/BuiltinFunction.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/BuiltinFunction.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; /** All built-in UDFs need to register their function names and classes here. */ public enum BuiltinFunction { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFAbs.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFAbs.java similarity index 81% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFAbs.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFAbs.java index 82ed1b765153..8de5c909ac80 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFAbs.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFAbs.java @@ -17,15 +17,15 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; import org.apache.iotdb.commons.exception.MetadataException; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; -import org.apache.iotdb.db.query.udf.api.exception.UDFInputSeriesDataTypeNotValidException; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.exception.UDFInputSeriesDataTypeNotValidException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.io.IOException; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFAcos.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFAcos.java similarity index 95% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFAcos.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFAcos.java index dbcd5b5c9854..1491c239dd22 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFAcos.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFAcos.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; public class UDTFAcos extends UDTFMath { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFAsin.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFAsin.java similarity index 95% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFAsin.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFAsin.java index 7e375da6f5a2..f83394a7c189 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFAsin.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFAsin.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; public class UDTFAsin extends UDTFMath { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFAtan.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFAtan.java similarity index 95% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFAtan.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFAtan.java index 8f03ae84ae56..e36a04ea4896 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFAtan.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFAtan.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; public class UDTFAtan extends UDTFMath { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFBottomK.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFBottomK.java similarity index 79% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFBottomK.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFBottomK.java index ccf21080b806..dcc692f84291 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFBottomK.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFBottomK.java @@ -17,15 +17,14 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; -import org.apache.iotdb.db.query.udf.api.exception.UDFInputSeriesDataTypeNotValidException; +import org.apache.iotdb.commons.udf.api.exception.UDFInputSeriesDataTypeNotValidException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.utils.Pair; -import org.apache.commons.collections4.ComparatorUtils; - import java.util.Comparator; +import java.util.Objects; import java.util.PriorityQueue; public class UDTFBottomK extends UDTFSelectK { @@ -48,7 +47,25 @@ protected void constructPQ() throws UDFInputSeriesDataTypeNotValidException { case TEXT: stringPQ = new PriorityQueue<>( - k, ComparatorUtils.reversedComparator(Comparator.comparing(o -> o.right))); + k, + (pairA, pairB) -> { + final String cs1 = pairB.right; + final String cs2 = pairA.right; + + if (Objects.requireNonNull(cs1) == Objects.requireNonNull(cs2)) { + return 0; + } + + for (int i = 0, len = Math.min(cs1.length(), cs2.length()); i < len; i++) { + final char a = cs1.charAt(i); + final char b = cs2.charAt(i); + if (a != b) { + return a - b; + } + } + + return cs1.length() - cs2.length(); + }); break; default: // This will not happen. diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFCast.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFCast.java similarity index 84% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFCast.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFCast.java index 2a2ad1e3e502..ca5745307208 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFCast.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFCast.java @@ -17,19 +17,18 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; import org.apache.iotdb.commons.exception.MetadataException; -import org.apache.iotdb.db.exception.query.QueryProcessException; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; -import org.apache.iotdb.db.query.udf.api.exception.UDFAttributeNotProvidedException; -import org.apache.iotdb.db.query.udf.api.exception.UDFInputSeriesNumberNotValidException; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.exception.UDFAttributeNotProvidedException; +import org.apache.iotdb.commons.udf.api.exception.UDFInputSeriesNumberNotValidException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.utils.Binary; @@ -58,8 +57,7 @@ public void beforeStart(UDFParameters parameters, UDTFConfigurations configurati } @Override - public void transform(Row row, PointCollector collector) - throws IOException, QueryProcessException { + public void transform(Row row, PointCollector collector) throws IOException { switch (sourceDataType) { case INT32: cast(row.getTime(), row.getInt(0), collector); @@ -84,8 +82,7 @@ public void transform(Row row, PointCollector collector) } } - private void cast(long time, int value, PointCollector collector) - throws IOException, QueryProcessException { + private void cast(long time, int value, PointCollector collector) throws IOException { switch (targetDataType) { case INT32: collector.putInt(time, value); @@ -110,8 +107,7 @@ private void cast(long time, int value, PointCollector collector) } } - private void cast(long time, long value, PointCollector collector) - throws IOException, QueryProcessException { + private void cast(long time, long value, PointCollector collector) throws IOException { switch (targetDataType) { case INT32: collector.putInt(time, (int) value); @@ -136,8 +132,7 @@ private void cast(long time, long value, PointCollector collector) } } - private void cast(long time, float value, PointCollector collector) - throws IOException, QueryProcessException { + private void cast(long time, float value, PointCollector collector) throws IOException { switch (targetDataType) { case INT32: collector.putInt(time, (int) value); @@ -162,8 +157,7 @@ private void cast(long time, float value, PointCollector collector) } } - private void cast(long time, double value, PointCollector collector) - throws IOException, QueryProcessException { + private void cast(long time, double value, PointCollector collector) throws IOException { switch (targetDataType) { case INT32: collector.putInt(time, (int) value); @@ -188,8 +182,7 @@ private void cast(long time, double value, PointCollector collector) } } - private void cast(long time, boolean value, PointCollector collector) - throws IOException, QueryProcessException { + private void cast(long time, boolean value, PointCollector collector) throws IOException { switch (targetDataType) { case INT32: collector.putInt(time, value ? 1 : 0); @@ -214,8 +207,7 @@ private void cast(long time, boolean value, PointCollector collector) } } - private void cast(long time, Binary value, PointCollector collector) - throws IOException, QueryProcessException { + private void cast(long time, Binary value, PointCollector collector) throws IOException { String stringValue = value.getStringValue(); switch (targetDataType) { case INT32: diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFCeil.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFCeil.java similarity index 95% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFCeil.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFCeil.java index 76b49b4adf3c..a4ba41c8b041 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFCeil.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFCeil.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; public class UDTFCeil extends UDTFMath { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFCommonDerivative.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFCommonDerivative.java similarity index 89% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFCommonDerivative.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFCommonDerivative.java index deb1c7b17b9b..4e3684a02abd 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFCommonDerivative.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFCommonDerivative.java @@ -17,11 +17,11 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.exception.UDFInputSeriesDataTypeNotValidException; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.exception.UDFInputSeriesDataTypeNotValidException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.io.IOException; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFCommonValueDifference.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFCommonValueDifference.java similarity index 89% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFCommonValueDifference.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFCommonValueDifference.java index 1dbd2222d42c..6a16a5173f3f 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFCommonValueDifference.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFCommonValueDifference.java @@ -17,11 +17,11 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.exception.UDFInputSeriesDataTypeNotValidException; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.exception.UDFInputSeriesDataTypeNotValidException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.io.IOException; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFConst.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFConst.java similarity index 85% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFConst.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFConst.java index 89784252f8d4..63849e2b0ca2 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFConst.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFConst.java @@ -17,16 +17,16 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; -import org.apache.iotdb.db.query.udf.api.exception.UDFParameterNotValidException; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.exception.UDFParameterNotValidException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.utils.Binary; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFConstE.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFConstE.java similarity index 73% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFConstE.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFConstE.java index 0e6ee376971f..c6d831de6929 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFConstE.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFConstE.java @@ -17,14 +17,14 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; public class UDTFConstE implements UDTF { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFConstPi.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFConstPi.java similarity index 73% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFConstPi.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFConstPi.java index c2724c7c7ecb..30d158b8d6f7 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFConstPi.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFConstPi.java @@ -17,14 +17,14 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; public class UDTFConstPi implements UDTF { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFContains.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFContains.java similarity index 72% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFContains.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFContains.java index 09ee073a605c..a9b0fd10c1c1 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFContains.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFContains.java @@ -17,16 +17,16 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; -import org.apache.iotdb.db.query.udf.api.exception.UDFException; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.exception.UDFException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; public class UDTFContains implements UDTF { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFContinuouslySatisfy.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFContinuouslySatisfy.java similarity index 91% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFContinuouslySatisfy.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFContinuouslySatisfy.java index 34569181c8e7..70d9d5480a91 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFContinuouslySatisfy.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFContinuouslySatisfy.java @@ -17,18 +17,18 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; import org.apache.iotdb.commons.exception.MetadataException; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; -import org.apache.iotdb.db.query.udf.api.exception.UDFException; -import org.apache.iotdb.db.query.udf.api.exception.UDFInputSeriesDataTypeNotValidException; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.exception.UDFException; +import org.apache.iotdb.commons.udf.api.exception.UDFInputSeriesDataTypeNotValidException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.utils.Pair; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFCos.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFCos.java similarity index 95% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFCos.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFCos.java index 0bfe2459fa72..777494050f50 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFCos.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFCos.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; public class UDTFCos extends UDTFMath { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFCosh.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFCosh.java similarity index 95% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFCosh.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFCosh.java index 01289d8f884f..028d727d82c6 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFCosh.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFCosh.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; public class UDTFCosh extends UDTFMath { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFDegrees.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFDegrees.java similarity index 95% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFDegrees.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFDegrees.java index 712278a46fdc..bfff41e1c03c 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFDegrees.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFDegrees.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; public class UDTFDegrees extends UDTFMath { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFDerivative.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFDerivative.java similarity index 77% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFDerivative.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFDerivative.java index ffadabb33924..f2308fd728f9 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFDerivative.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFDerivative.java @@ -17,15 +17,15 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; import org.apache.iotdb.commons.exception.MetadataException; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; -import org.apache.iotdb.db.query.udf.api.exception.UDFInputSeriesDataTypeNotValidException; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.exception.UDFInputSeriesDataTypeNotValidException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.io.IOException; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFEqualSizeBucketAggSample.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFEqualSizeBucketAggSample.java similarity index 95% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFEqualSizeBucketAggSample.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFEqualSizeBucketAggSample.java index 84fa4688ea35..1c26191dfa91 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFEqualSizeBucketAggSample.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFEqualSizeBucketAggSample.java @@ -17,18 +17,18 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; import org.apache.iotdb.commons.exception.MetadataException; -import org.apache.iotdb.db.query.udf.api.access.RowWindow; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; -import org.apache.iotdb.db.query.udf.api.exception.UDFException; -import org.apache.iotdb.db.query.udf.api.exception.UDFInputSeriesDataTypeNotValidException; -import org.apache.iotdb.db.query.udf.api.exception.UDFParameterNotValidException; +import org.apache.iotdb.commons.udf.api.access.RowWindow; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; +import org.apache.iotdb.commons.udf.api.exception.UDFException; +import org.apache.iotdb.commons.udf.api.exception.UDFInputSeriesDataTypeNotValidException; +import org.apache.iotdb.commons.udf.api.exception.UDFParameterNotValidException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.io.IOException; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFEqualSizeBucketM4Sample.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFEqualSizeBucketM4Sample.java similarity index 92% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFEqualSizeBucketM4Sample.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFEqualSizeBucketM4Sample.java index b4b7b2c1c514..0e8859e7f151 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFEqualSizeBucketM4Sample.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFEqualSizeBucketM4Sample.java @@ -17,16 +17,16 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; - -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.access.RowWindow; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; -import org.apache.iotdb.db.query.udf.api.exception.UDFException; -import org.apache.iotdb.db.query.udf.api.exception.UDFInputSeriesDataTypeNotValidException; +package org.apache.iotdb.commons.udf.builtin; + +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.access.RowWindow; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; +import org.apache.iotdb.commons.udf.api.exception.UDFException; +import org.apache.iotdb.commons.udf.api.exception.UDFInputSeriesDataTypeNotValidException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.io.IOException; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFEqualSizeBucketOutlierSample.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFEqualSizeBucketOutlierSample.java similarity index 97% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFEqualSizeBucketOutlierSample.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFEqualSizeBucketOutlierSample.java index 249c46ecb115..ff33170fc002 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFEqualSizeBucketOutlierSample.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFEqualSizeBucketOutlierSample.java @@ -17,19 +17,19 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; import org.apache.iotdb.commons.exception.MetadataException; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.access.RowWindow; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; -import org.apache.iotdb.db.query.udf.api.exception.UDFException; -import org.apache.iotdb.db.query.udf.api.exception.UDFInputSeriesDataTypeNotValidException; -import org.apache.iotdb.db.query.udf.api.exception.UDFParameterNotValidException; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.access.RowWindow; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; +import org.apache.iotdb.commons.udf.api.exception.UDFException; +import org.apache.iotdb.commons.udf.api.exception.UDFInputSeriesDataTypeNotValidException; +import org.apache.iotdb.commons.udf.api.exception.UDFParameterNotValidException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.utils.Pair; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFEqualSizeBucketRandomSample.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFEqualSizeBucketRandomSample.java similarity index 78% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFEqualSizeBucketRandomSample.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFEqualSizeBucketRandomSample.java index 3b45f194c8f1..e156b1622c6b 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFEqualSizeBucketRandomSample.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFEqualSizeBucketRandomSample.java @@ -17,15 +17,15 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.access.RowWindow; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; -import org.apache.iotdb.db.query.udf.api.exception.UDFInputSeriesDataTypeNotValidException; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.access.RowWindow; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; +import org.apache.iotdb.commons.udf.api.exception.UDFInputSeriesDataTypeNotValidException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.io.IOException; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFEqualSizeBucketSample.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFEqualSizeBucketSample.java similarity index 87% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFEqualSizeBucketSample.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFEqualSizeBucketSample.java index 78f1fa57c071..447c38d9b70b 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFEqualSizeBucketSample.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFEqualSizeBucketSample.java @@ -17,12 +17,12 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; import org.apache.iotdb.commons.exception.MetadataException; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.exception.UDFException; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.exception.UDFException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; public abstract class UDTFEqualSizeBucketSample implements UDTF { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFExp.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFExp.java similarity index 95% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFExp.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFExp.java index e0e16ef7b82b..f86c2269195a 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFExp.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFExp.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; public class UDTFExp extends UDTFMath { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFFloor.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFFloor.java similarity index 95% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFFloor.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFFloor.java index 92b6063fb491..b9e392df3b85 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFFloor.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFFloor.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; public class UDTFFloor extends UDTFMath { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFInRange.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFInRange.java similarity index 81% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFInRange.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFInRange.java index 422bfd9d216e..fd2820b9c29c 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFInRange.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFInRange.java @@ -17,18 +17,18 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; import org.apache.iotdb.commons.exception.MetadataException; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; -import org.apache.iotdb.db.query.udf.api.exception.UDFException; -import org.apache.iotdb.db.query.udf.api.exception.UDFInputSeriesDataTypeNotValidException; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.exception.UDFException; +import org.apache.iotdb.commons.udf.api.exception.UDFInputSeriesDataTypeNotValidException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.io.IOException; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFJexl.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFJexl.java similarity index 86% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFJexl.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFJexl.java index 20a529e31d9a..04cabac29e59 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFJexl.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFJexl.java @@ -17,20 +17,19 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; import org.apache.iotdb.commons.exception.MetadataException; -import org.apache.iotdb.db.exception.query.QueryProcessException; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; -import org.apache.iotdb.db.query.udf.api.exception.UDFException; -import org.apache.iotdb.db.query.udf.api.exception.UDFInputSeriesDataTypeNotValidException; -import org.apache.iotdb.db.query.udf.api.exception.UDFOutputSeriesDataTypeNotValidException; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.exception.UDFException; +import org.apache.iotdb.commons.udf.api.exception.UDFInputSeriesDataTypeNotValidException; +import org.apache.iotdb.commons.udf.api.exception.UDFOutputSeriesDataTypeNotValidException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.commons.jexl3.JexlBuilder; @@ -120,7 +119,7 @@ public void beforeStart(UDFParameters parameters, UDTFConfigurations configurati // 23, 23L, 23f, 23d, "string", true are hard codes for probing private HashMap initialMap() { - HashMap map = new HashMap(); + HashMap map = new HashMap<>(); map.put(TSDataType.INT32, 23); map.put(TSDataType.INT64, 23L); map.put(TSDataType.FLOAT, 23f); @@ -153,7 +152,7 @@ private TSDataType probeOutputDataType() throws UDFOutputSeriesDataTypeNotValidE @Override public void transform(Row row, PointCollector collector) - throws IOException, UDFOutputSeriesDataTypeNotValidException, QueryProcessException, + throws IOException, UDFOutputSeriesDataTypeNotValidException, UDFInputSeriesDataTypeNotValidException { switch (outputDataType) { case DOUBLE: @@ -176,7 +175,7 @@ void evaluateDouble(Row row, PointCollector collector) throws IOException, UDFInputSeriesDataTypeNotValidException; void evaluateText(Row row, PointCollector collector) - throws IOException, QueryProcessException, UDFInputSeriesDataTypeNotValidException; + throws IOException, UDFInputSeriesDataTypeNotValidException; void evaluateBoolean(Row row, PointCollector collector) throws IOException, UDFInputSeriesDataTypeNotValidException; @@ -190,8 +189,7 @@ public void evaluateDouble(Row row, PointCollector collector) throws IOException } @Override - public void evaluateText(Row row, PointCollector collector) - throws IOException, QueryProcessException { + public void evaluateText(Row row, PointCollector collector) throws IOException { collector.putString(row.getTime(), (String) script.execute(null, row.getInt(0))); } @@ -209,8 +207,7 @@ public void evaluateDouble(Row row, PointCollector collector) throws IOException } @Override - public void evaluateText(Row row, PointCollector collector) - throws IOException, QueryProcessException { + public void evaluateText(Row row, PointCollector collector) throws IOException { collector.putString(row.getTime(), (String) script.execute(null, row.getLong(0))); } @@ -228,8 +225,7 @@ public void evaluateDouble(Row row, PointCollector collector) throws IOException } @Override - public void evaluateText(Row row, PointCollector collector) - throws IOException, QueryProcessException { + public void evaluateText(Row row, PointCollector collector) throws IOException { collector.putString(row.getTime(), (String) script.execute(null, row.getFloat(0))); } @@ -247,8 +243,7 @@ public void evaluateDouble(Row row, PointCollector collector) throws IOException } @Override - public void evaluateText(Row row, PointCollector collector) - throws IOException, QueryProcessException { + public void evaluateText(Row row, PointCollector collector) throws IOException { collector.putString(row.getTime(), (String) script.execute(null, row.getDouble(0))); } @@ -266,8 +261,7 @@ public void evaluateDouble(Row row, PointCollector collector) throws IOException } @Override - public void evaluateText(Row row, PointCollector collector) - throws IOException, QueryProcessException { + public void evaluateText(Row row, PointCollector collector) throws IOException { collector.putString(row.getTime(), (String) script.execute(null, row.getString(0))); } @@ -285,8 +279,7 @@ public void evaluateDouble(Row row, PointCollector collector) throws IOException } @Override - public void evaluateText(Row row, PointCollector collector) - throws IOException, QueryProcessException { + public void evaluateText(Row row, PointCollector collector) throws IOException { collector.putString(row.getTime(), (String) script.execute(null, row.getBoolean(0))); } @@ -309,7 +302,7 @@ public void evaluateDouble(Row row, PointCollector collector) @Override public void evaluateText(Row row, PointCollector collector) - throws IOException, QueryProcessException, UDFInputSeriesDataTypeNotValidException { + throws IOException, UDFInputSeriesDataTypeNotValidException { getValues(row); collector.putString(row.getTime(), (String) script.execute(null, values)); } diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFLog.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFLog.java similarity index 95% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFLog.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFLog.java index b96889365456..7d0296fec0eb 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFLog.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFLog.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; public class UDTFLog extends UDTFMath { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFLog10.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFLog10.java similarity index 95% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFLog10.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFLog10.java index 80d3546bf040..68ae84001566 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFLog10.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFLog10.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; public class UDTFLog10 extends UDTFMath { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFMatches.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFMatches.java similarity index 73% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFMatches.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFMatches.java index cecb9db9929c..53301ad0cade 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFMatches.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFMatches.java @@ -17,16 +17,16 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; - -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; -import org.apache.iotdb.db.query.udf.api.exception.UDFException; +package org.apache.iotdb.commons.udf.builtin; + +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.exception.UDFException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.util.regex.Pattern; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFMath.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFMath.java similarity index 79% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFMath.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFMath.java index 20c696339501..3e0b42fc46cf 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFMath.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFMath.java @@ -17,18 +17,18 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; import org.apache.iotdb.commons.exception.MetadataException; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; -import org.apache.iotdb.db.query.udf.api.exception.UDFException; -import org.apache.iotdb.db.query.udf.api.exception.UDFInputSeriesDataTypeNotValidException; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.exception.UDFException; +import org.apache.iotdb.commons.udf.api.exception.UDFInputSeriesDataTypeNotValidException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.io.IOException; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFNonNegativeDerivative.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFNonNegativeDerivative.java similarity index 89% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFNonNegativeDerivative.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFNonNegativeDerivative.java index 8e44c50eaf32..066624973535 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFNonNegativeDerivative.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFNonNegativeDerivative.java @@ -17,11 +17,11 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.exception.UDFInputSeriesDataTypeNotValidException; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.exception.UDFInputSeriesDataTypeNotValidException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.io.IOException; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFNonNegativeValueDifference.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFNonNegativeValueDifference.java similarity index 89% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFNonNegativeValueDifference.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFNonNegativeValueDifference.java index 0a6549a5723e..033d31621007 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFNonNegativeValueDifference.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFNonNegativeValueDifference.java @@ -17,11 +17,11 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.exception.UDFInputSeriesDataTypeNotValidException; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.exception.UDFInputSeriesDataTypeNotValidException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.io.IOException; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFNonZeroCount.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFNonZeroCount.java similarity index 97% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFNonZeroCount.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFNonZeroCount.java index d777c69e1637..f88dc376cf97 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFNonZeroCount.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFNonZeroCount.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; public class UDTFNonZeroCount extends UDTFContinuouslySatisfy { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFNonZeroDuration.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFNonZeroDuration.java similarity index 97% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFNonZeroDuration.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFNonZeroDuration.java index 145dc2a8b28f..61407180c418 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFNonZeroDuration.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFNonZeroDuration.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; public class UDTFNonZeroDuration extends UDTFContinuouslySatisfy { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFOnOff.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFOnOff.java similarity index 80% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFOnOff.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFOnOff.java index fa2e9bff8d2c..f742225883a8 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFOnOff.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFOnOff.java @@ -17,17 +17,17 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; import org.apache.iotdb.commons.exception.MetadataException; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; -import org.apache.iotdb.db.query.udf.api.exception.UDFException; -import org.apache.iotdb.db.query.udf.api.exception.UDFInputSeriesDataTypeNotValidException; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.exception.UDFException; +import org.apache.iotdb.commons.udf.api.exception.UDFInputSeriesDataTypeNotValidException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.io.IOException; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFRadians.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFRadians.java similarity index 95% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFRadians.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFRadians.java index 150ad0eb60fd..d866e1b013e1 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFRadians.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFRadians.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; public class UDTFRadians extends UDTFMath { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFRound.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFRound.java similarity index 95% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFRound.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFRound.java index 6587f2ac3a4c..85adb5f43715 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFRound.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFRound.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; public class UDTFRound extends UDTFMath { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFSelectK.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFSelectK.java similarity index 87% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFSelectK.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFSelectK.java index 6e7b2a5cc17d..fef9908d57fc 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFSelectK.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFSelectK.java @@ -17,19 +17,18 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; import org.apache.iotdb.commons.exception.MetadataException; -import org.apache.iotdb.db.exception.query.QueryProcessException; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; -import org.apache.iotdb.db.query.udf.api.exception.UDFException; -import org.apache.iotdb.db.query.udf.api.exception.UDFInputSeriesDataTypeNotValidException; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.exception.UDFException; +import org.apache.iotdb.commons.udf.api.exception.UDFInputSeriesDataTypeNotValidException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.utils.Pair; @@ -121,7 +120,7 @@ public void transform(Row row, PointCollector collector) @Override public void terminate(PointCollector collector) - throws UDFInputSeriesDataTypeNotValidException, IOException, QueryProcessException { + throws UDFInputSeriesDataTypeNotValidException, IOException { switch (dataType) { case INT32: for (Pair pair : diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFSign.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFSign.java similarity index 95% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFSign.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFSign.java index ea9318691d8a..633f55c6e620 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFSign.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFSign.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; public class UDTFSign extends UDTFMath { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFSin.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFSin.java similarity index 95% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFSin.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFSin.java index 882daac719f3..d141a7c967ee 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFSin.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFSin.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; public class UDTFSin extends UDTFMath { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFSinh.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFSinh.java similarity index 95% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFSinh.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFSinh.java index 5403c3fbc59a..b25cf132bf4c 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFSinh.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFSinh.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; public class UDTFSinh extends UDTFMath { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFSqrt.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFSqrt.java similarity index 95% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFSqrt.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFSqrt.java index c246f6270dbf..9958998205e1 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFSqrt.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFSqrt.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; public class UDTFSqrt extends UDTFMath { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFTan.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFTan.java similarity index 95% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFTan.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFTan.java index b5f5bf9411e0..984c6d7607fe 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFTan.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFTan.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; public class UDTFTan extends UDTFMath { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFTanh.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFTanh.java similarity index 95% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFTanh.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFTanh.java index 034cd6725987..f9291d4aee85 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFTanh.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFTanh.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; public class UDTFTanh extends UDTFMath { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFTimeDifference.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFTimeDifference.java similarity index 77% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFTimeDifference.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFTimeDifference.java index ee2c69c7a426..6b13904a5507 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFTimeDifference.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFTimeDifference.java @@ -17,14 +17,14 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; - -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +package org.apache.iotdb.commons.udf.builtin; + +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.io.IOException; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFTopK.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFTopK.java similarity index 96% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFTopK.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFTopK.java index 3f35fe6af669..266100b67dfc 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFTopK.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFTopK.java @@ -17,9 +17,9 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; -import org.apache.iotdb.db.query.udf.api.exception.UDFInputSeriesDataTypeNotValidException; +import org.apache.iotdb.commons.udf.api.exception.UDFInputSeriesDataTypeNotValidException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.utils.Pair; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFValueDifference.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFValueDifference.java similarity index 75% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFValueDifference.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFValueDifference.java index 0d7d41b5ba4c..6c48eb7104ed 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFValueDifference.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFValueDifference.java @@ -17,15 +17,15 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; import org.apache.iotdb.commons.exception.MetadataException; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; -import org.apache.iotdb.db.query.udf.api.exception.UDFInputSeriesDataTypeNotValidException; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.exception.UDFInputSeriesDataTypeNotValidException; import java.io.IOException; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFValueTrend.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFValueTrend.java similarity index 83% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFValueTrend.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFValueTrend.java index 8862ef1e0321..d872df87623e 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFValueTrend.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFValueTrend.java @@ -17,14 +17,14 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.exception.UDFException; -import org.apache.iotdb.db.query.udf.api.exception.UDFInputSeriesDataTypeNotValidException; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.exception.UDFException; +import org.apache.iotdb.commons.udf.api.exception.UDFInputSeriesDataTypeNotValidException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.io.IOException; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFZeroCount.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFZeroCount.java similarity index 97% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFZeroCount.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFZeroCount.java index 3a7499d8eb68..83add4046222 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFZeroCount.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFZeroCount.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; public class UDTFZeroCount extends UDTFContinuouslySatisfy { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFZeroDuration.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFZeroDuration.java similarity index 97% rename from server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFZeroDuration.java rename to node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFZeroDuration.java index bacc1ed74040..a0e40f25c328 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFZeroDuration.java +++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFZeroDuration.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.builtin; +package org.apache.iotdb.commons.udf.builtin; public class UDTFZeroDuration extends UDTFContinuouslySatisfy { diff --git a/server/src/main/java/org/apache/iotdb/db/engine/selectinto/InsertTabletPlansIterator.java b/server/src/main/java/org/apache/iotdb/db/engine/selectinto/InsertTabletPlansIterator.java index fd309d667d58..da291e6ef471 100644 --- a/server/src/main/java/org/apache/iotdb/db/engine/selectinto/InsertTabletPlansIterator.java +++ b/server/src/main/java/org/apache/iotdb/db/engine/selectinto/InsertTabletPlansIterator.java @@ -22,9 +22,9 @@ import org.apache.iotdb.commons.exception.IllegalPathException; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.conf.IoTDBDescriptor; +import org.apache.iotdb.db.mpp.plan.expression.ResultColumn; import org.apache.iotdb.db.qp.physical.crud.InsertTabletPlan; import org.apache.iotdb.db.qp.physical.crud.QueryPlan; -import org.apache.iotdb.db.query.expression.ResultColumn; import org.apache.iotdb.tsfile.read.common.Path; import org.apache.iotdb.tsfile.read.common.RowRecord; import org.apache.iotdb.tsfile.read.query.dataset.QueryDataSet; diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/FilterOperator.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/FilterOperator.java index 8522d065cc83..b9050c27ad96 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/FilterOperator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/FilterOperator.java @@ -23,9 +23,9 @@ import org.apache.iotdb.db.mpp.execution.operator.Operator; import org.apache.iotdb.db.mpp.execution.operator.OperatorContext; import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; +import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.InputLocation; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; import org.apache.iotdb.tsfile.exception.write.UnSupportedDataTypeException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.read.common.block.TsBlock; diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/TransformOperator.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/TransformOperator.java index 784573cd1bbf..ed0dbb0f8628 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/TransformOperator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/TransformOperator.java @@ -24,13 +24,13 @@ import org.apache.iotdb.db.mpp.execution.operator.Operator; import org.apache.iotdb.db.mpp.execution.operator.OperatorContext; import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; +import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.InputLocation; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.udf.core.executor.UDTFContext; -import org.apache.iotdb.db.query.udf.core.layer.EvaluationDAGBuilder; -import org.apache.iotdb.db.query.udf.core.layer.RawQueryInputLayer; -import org.apache.iotdb.db.query.udf.core.layer.TsBlockInputDataSet; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.dag.builder.EvaluationDAGBuilder; +import org.apache.iotdb.db.mpp.transformation.dag.input.QueryDataSetInputLayer; +import org.apache.iotdb.db.mpp.transformation.dag.input.TsBlockInputDataSet; +import org.apache.iotdb.db.mpp.transformation.dag.udf.UDTFContext; import org.apache.iotdb.db.query.udf.service.UDFClassLoaderManager; import org.apache.iotdb.db.query.udf.service.UDFRegistrationService; import org.apache.iotdb.db.utils.datastructure.TimeSelector; @@ -67,7 +67,7 @@ public class TransformOperator implements ProcessOperator { protected boolean isFirstIteration; - protected RawQueryInputLayer inputLayer; + protected QueryDataSetInputLayer inputLayer; protected UDTFContext udtfContext; protected LayerPointReader[] transformers; protected TimeSelector timeHeap; @@ -96,7 +96,7 @@ public TransformOperator( private void initInputLayer(List inputDataTypes) throws QueryProcessException { inputLayer = - new RawQueryInputLayer( + new QueryDataSetInputLayer( operatorContext.getOperatorId(), udfReaderMemoryBudgetInMB, new TsBlockInputDataSet(inputOperator, inputDataTypes)); diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analysis.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analysis.java index ded3afa59c73..450bda895dd5 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analysis.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analysis.java @@ -25,11 +25,11 @@ import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.mpp.common.header.DatasetHeader; import org.apache.iotdb.db.mpp.common.schematree.SchemaTree; +import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.FillDescriptor; import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.FilterNullParameter; import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.GroupByTimeParameter; import org.apache.iotdb.db.mpp.plan.statement.Statement; -import org.apache.iotdb.db.query.expression.Expression; import org.apache.iotdb.tsfile.read.filter.basic.Filter; import java.util.List; diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analyzer.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analyzer.java index a47f4fce44c7..884b345b4b42 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analyzer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analyzer.java @@ -35,6 +35,7 @@ import org.apache.iotdb.db.mpp.common.schematree.DeviceSchemaInfo; import org.apache.iotdb.db.mpp.common.schematree.PathPatternTree; import org.apache.iotdb.db.mpp.common.schematree.SchemaTree; +import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.FillDescriptor; import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.FilterNullParameter; import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.GroupByTimeParameter; @@ -68,7 +69,6 @@ import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowStorageGroupStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowTTLStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowTimeSeriesStatement; -import org.apache.iotdb.db.query.expression.Expression; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.read.filter.GroupByFilter; import org.apache.iotdb.tsfile.read.filter.basic.Filter; diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ConcatPathRewriter.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ConcatPathRewriter.java index f38351a50cd6..f3816fd289a7 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ConcatPathRewriter.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ConcatPathRewriter.java @@ -22,12 +22,12 @@ import org.apache.iotdb.db.exception.sql.SemanticException; import org.apache.iotdb.db.exception.sql.StatementAnalyzeException; import org.apache.iotdb.db.mpp.common.schematree.PathPatternTree; +import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.db.mpp.plan.statement.Statement; import org.apache.iotdb.db.mpp.plan.statement.component.FilterNullComponent; import org.apache.iotdb.db.mpp.plan.statement.component.ResultColumn; import org.apache.iotdb.db.mpp.plan.statement.component.SelectComponent; import org.apache.iotdb.db.mpp.plan.statement.crud.QueryStatement; -import org.apache.iotdb.db.query.expression.Expression; import java.util.ArrayList; import java.util.List; diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ExpressionAnalyzer.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ExpressionAnalyzer.java index 104545e7fea9..0c5cfc5b1df6 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ExpressionAnalyzer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ExpressionAnalyzer.java @@ -25,27 +25,27 @@ import org.apache.iotdb.db.metadata.path.MeasurementPath; import org.apache.iotdb.db.mpp.common.schematree.PathPatternTree; import org.apache.iotdb.db.mpp.common.schematree.SchemaTree; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ExpressionType; +import org.apache.iotdb.db.mpp.plan.expression.binary.BinaryExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.EqualToExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.GreaterEqualExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.GreaterThanExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.LessEqualExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.LessThanExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.LogicAndExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.LogicOrExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.NonEqualExpression; +import org.apache.iotdb.db.mpp.plan.expression.leaf.ConstantOperand; +import org.apache.iotdb.db.mpp.plan.expression.leaf.LeafOperand; +import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand; +import org.apache.iotdb.db.mpp.plan.expression.leaf.TimestampOperand; +import org.apache.iotdb.db.mpp.plan.expression.multi.FunctionExpression; +import org.apache.iotdb.db.mpp.plan.expression.unary.InExpression; +import org.apache.iotdb.db.mpp.plan.expression.unary.LogicNotExpression; +import org.apache.iotdb.db.mpp.plan.expression.unary.UnaryExpression; import org.apache.iotdb.db.mpp.plan.statement.component.ResultColumn; import org.apache.iotdb.db.qp.constant.SQLConstant; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.ExpressionType; -import org.apache.iotdb.db.query.expression.binary.BinaryExpression; -import org.apache.iotdb.db.query.expression.binary.EqualToExpression; -import org.apache.iotdb.db.query.expression.binary.GreaterEqualExpression; -import org.apache.iotdb.db.query.expression.binary.GreaterThanExpression; -import org.apache.iotdb.db.query.expression.binary.LessEqualExpression; -import org.apache.iotdb.db.query.expression.binary.LessThanExpression; -import org.apache.iotdb.db.query.expression.binary.LogicAndExpression; -import org.apache.iotdb.db.query.expression.binary.LogicOrExpression; -import org.apache.iotdb.db.query.expression.binary.NonEqualExpression; -import org.apache.iotdb.db.query.expression.leaf.ConstantOperand; -import org.apache.iotdb.db.query.expression.leaf.LeafOperand; -import org.apache.iotdb.db.query.expression.leaf.TimeSeriesOperand; -import org.apache.iotdb.db.query.expression.leaf.TimestampOperand; -import org.apache.iotdb.db.query.expression.multi.FunctionExpression; -import org.apache.iotdb.db.query.expression.unary.InExpression; -import org.apache.iotdb.db.query.expression.unary.LogicNotExpression; -import org.apache.iotdb.db.query.expression.unary.UnaryExpression; import org.apache.iotdb.tsfile.common.constant.TsFileConstant; import org.apache.iotdb.tsfile.read.filter.TimeFilter; import org.apache.iotdb.tsfile.read.filter.basic.Filter; diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ExpressionUtils.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ExpressionUtils.java index 2618d6ab6ec1..7664c51e5f74 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ExpressionUtils.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ExpressionUtils.java @@ -20,31 +20,31 @@ package org.apache.iotdb.db.mpp.plan.analyze; import org.apache.iotdb.commons.path.PartialPath; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.ExpressionType; -import org.apache.iotdb.db.query.expression.binary.AdditionExpression; -import org.apache.iotdb.db.query.expression.binary.DivisionExpression; -import org.apache.iotdb.db.query.expression.binary.EqualToExpression; -import org.apache.iotdb.db.query.expression.binary.GreaterEqualExpression; -import org.apache.iotdb.db.query.expression.binary.GreaterThanExpression; -import org.apache.iotdb.db.query.expression.binary.LessEqualExpression; -import org.apache.iotdb.db.query.expression.binary.LessThanExpression; -import org.apache.iotdb.db.query.expression.binary.LogicAndExpression; -import org.apache.iotdb.db.query.expression.binary.LogicOrExpression; -import org.apache.iotdb.db.query.expression.binary.ModuloExpression; -import org.apache.iotdb.db.query.expression.binary.MultiplicationExpression; -import org.apache.iotdb.db.query.expression.binary.NonEqualExpression; -import org.apache.iotdb.db.query.expression.binary.SubtractionExpression; -import org.apache.iotdb.db.query.expression.leaf.ConstantOperand; -import org.apache.iotdb.db.query.expression.leaf.TimeSeriesOperand; -import org.apache.iotdb.db.query.expression.leaf.TimestampOperand; -import org.apache.iotdb.db.query.expression.multi.FunctionExpression; -import org.apache.iotdb.db.query.expression.unary.InExpression; -import org.apache.iotdb.db.query.expression.unary.LikeExpression; -import org.apache.iotdb.db.query.expression.unary.LogicNotExpression; -import org.apache.iotdb.db.query.expression.unary.NegationExpression; -import org.apache.iotdb.db.query.expression.unary.RegularExpression; -import org.apache.iotdb.db.query.expression.unary.UnaryExpression; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ExpressionType; +import org.apache.iotdb.db.mpp.plan.expression.binary.AdditionExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.DivisionExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.EqualToExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.GreaterEqualExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.GreaterThanExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.LessEqualExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.LessThanExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.LogicAndExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.LogicOrExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.ModuloExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.MultiplicationExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.NonEqualExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.SubtractionExpression; +import org.apache.iotdb.db.mpp.plan.expression.leaf.ConstantOperand; +import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand; +import org.apache.iotdb.db.mpp.plan.expression.leaf.TimestampOperand; +import org.apache.iotdb.db.mpp.plan.expression.multi.FunctionExpression; +import org.apache.iotdb.db.mpp.plan.expression.unary.InExpression; +import org.apache.iotdb.db.mpp.plan.expression.unary.LikeExpression; +import org.apache.iotdb.db.mpp.plan.expression.unary.LogicNotExpression; +import org.apache.iotdb.db.mpp.plan.expression.unary.NegationExpression; +import org.apache.iotdb.db.mpp.plan.expression.unary.RegularExpression; +import org.apache.iotdb.db.mpp.plan.expression.unary.UnaryExpression; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.read.filter.TimeFilter; import org.apache.iotdb.tsfile.read.filter.basic.Filter; diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/GroupByLevelController.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/GroupByLevelController.java index 7be09fd68eeb..1aa25ce06fec 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/GroupByLevelController.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/GroupByLevelController.java @@ -23,9 +23,9 @@ import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.exception.sql.SemanticException; import org.apache.iotdb.db.exception.sql.StatementAnalyzeException; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.leaf.TimeSeriesOperand; -import org.apache.iotdb.db.query.expression.multi.FunctionExpression; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand; +import org.apache.iotdb.db.mpp.plan.expression.multi.FunctionExpression; import java.util.ArrayList; import java.util.Collections; diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/Expression.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/Expression.java similarity index 85% rename from server/src/main/java/org/apache/iotdb/db/query/expression/Expression.java rename to server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/Expression.java index ee460b61b401..bf0b1efcf793 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/Expression.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/Expression.java @@ -17,42 +17,42 @@ * under the License. */ -package org.apache.iotdb.db.query.expression; +package org.apache.iotdb.db.mpp.plan.expression; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.exception.query.LogicalOptimizeException; import org.apache.iotdb.db.exception.query.QueryProcessException; import org.apache.iotdb.db.exception.sql.SemanticException; import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; +import org.apache.iotdb.db.mpp.plan.expression.binary.AdditionExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.DivisionExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.EqualToExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.GreaterEqualExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.GreaterThanExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.LessEqualExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.LessThanExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.LogicAndExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.LogicOrExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.ModuloExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.MultiplicationExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.NonEqualExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.SubtractionExpression; +import org.apache.iotdb.db.mpp.plan.expression.leaf.ConstantOperand; +import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand; +import org.apache.iotdb.db.mpp.plan.expression.leaf.TimestampOperand; +import org.apache.iotdb.db.mpp.plan.expression.multi.FunctionExpression; +import org.apache.iotdb.db.mpp.plan.expression.unary.InExpression; +import org.apache.iotdb.db.mpp.plan.expression.unary.LikeExpression; +import org.apache.iotdb.db.mpp.plan.expression.unary.LogicNotExpression; +import org.apache.iotdb.db.mpp.plan.expression.unary.NegationExpression; +import org.apache.iotdb.db.mpp.plan.expression.unary.RegularExpression; import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.InputLocation; +import org.apache.iotdb.db.mpp.transformation.dag.input.QueryDataSetInputLayer; +import org.apache.iotdb.db.mpp.transformation.dag.intermediate.IntermediateLayer; +import org.apache.iotdb.db.mpp.transformation.dag.memory.LayerMemoryAssigner; +import org.apache.iotdb.db.mpp.transformation.dag.udf.UDTFContext; +import org.apache.iotdb.db.mpp.transformation.dag.udf.UDTFExecutor; import org.apache.iotdb.db.qp.physical.crud.UDTFPlan; -import org.apache.iotdb.db.query.expression.binary.AdditionExpression; -import org.apache.iotdb.db.query.expression.binary.DivisionExpression; -import org.apache.iotdb.db.query.expression.binary.EqualToExpression; -import org.apache.iotdb.db.query.expression.binary.GreaterEqualExpression; -import org.apache.iotdb.db.query.expression.binary.GreaterThanExpression; -import org.apache.iotdb.db.query.expression.binary.LessEqualExpression; -import org.apache.iotdb.db.query.expression.binary.LessThanExpression; -import org.apache.iotdb.db.query.expression.binary.LogicAndExpression; -import org.apache.iotdb.db.query.expression.binary.LogicOrExpression; -import org.apache.iotdb.db.query.expression.binary.ModuloExpression; -import org.apache.iotdb.db.query.expression.binary.MultiplicationExpression; -import org.apache.iotdb.db.query.expression.binary.NonEqualExpression; -import org.apache.iotdb.db.query.expression.binary.SubtractionExpression; -import org.apache.iotdb.db.query.expression.leaf.ConstantOperand; -import org.apache.iotdb.db.query.expression.leaf.TimeSeriesOperand; -import org.apache.iotdb.db.query.expression.leaf.TimestampOperand; -import org.apache.iotdb.db.query.expression.multi.FunctionExpression; -import org.apache.iotdb.db.query.expression.unary.InExpression; -import org.apache.iotdb.db.query.expression.unary.LikeExpression; -import org.apache.iotdb.db.query.expression.unary.LogicNotExpression; -import org.apache.iotdb.db.query.expression.unary.NegationExpression; -import org.apache.iotdb.db.query.expression.unary.RegularExpression; -import org.apache.iotdb.db.query.udf.core.executor.UDTFContext; -import org.apache.iotdb.db.query.udf.core.executor.UDTFExecutor; -import org.apache.iotdb.db.query.udf.core.layer.IntermediateLayer; -import org.apache.iotdb.db.query.udf.core.layer.LayerMemoryAssigner; -import org.apache.iotdb.db.query.udf.core.layer.RawQueryInputLayer; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils; @@ -150,7 +150,7 @@ public abstract void bindInputLayerColumnIndexWithExpression( public abstract IntermediateLayer constructIntermediateLayer( long queryId, UDTFContext udtfContext, - RawQueryInputLayer rawTimeSeriesInputLayer, + QueryDataSetInputLayer rawTimeSeriesInputLayer, Map expressionIntermediateLayerMap, Map expressionDataTypeMap, LayerMemoryAssigner memoryAssigner) @@ -159,7 +159,7 @@ public abstract IntermediateLayer constructIntermediateLayer( public abstract IntermediateLayer constructIntermediateLayer( long queryId, UDTFContext udtfContext, - RawQueryInputLayer rawTimeSeriesInputLayer, + QueryDataSetInputLayer rawTimeSeriesInputLayer, Map expressionIntermediateLayerMap, TypeProvider typeProvider, LayerMemoryAssigner memoryAssigner) diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/ExpressionType.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/ExpressionType.java similarity index 97% rename from server/src/main/java/org/apache/iotdb/db/query/expression/ExpressionType.java rename to server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/ExpressionType.java index 180afeecc6f2..4d3352da2b4b 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/ExpressionType.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/ExpressionType.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.iotdb.db.query.expression; +package org.apache.iotdb.db.mpp.plan.expression; public enum ExpressionType { CONSTANT((short) -4, (short) 1400), diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/ResultColumn.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/ResultColumn.java similarity index 99% rename from server/src/main/java/org/apache/iotdb/db/query/expression/ResultColumn.java rename to server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/ResultColumn.java index 32f7f92718a7..791da53e6ea9 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/ResultColumn.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/ResultColumn.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.expression; +package org.apache.iotdb.db.mpp.plan.expression; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.exception.query.LogicalOptimizeException; diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/AdditionExpression.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/AdditionExpression.java similarity index 78% rename from server/src/main/java/org/apache/iotdb/db/query/expression/binary/AdditionExpression.java rename to server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/AdditionExpression.java index 6fd5e698fc8d..d4c80fe95579 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/AdditionExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/AdditionExpression.java @@ -17,13 +17,13 @@ * under the License. */ -package org.apache.iotdb.db.query.expression.binary; +package org.apache.iotdb.db.mpp.plan.expression.binary; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.ExpressionType; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; -import org.apache.iotdb.db.query.udf.core.transformer.binary.ArithmeticAdditionTransformer; -import org.apache.iotdb.db.query.udf.core.transformer.binary.ArithmeticBinaryTransformer; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ExpressionType; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.binary.ArithmeticAdditionTransformer; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.binary.ArithmeticBinaryTransformer; import java.nio.ByteBuffer; diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/ArithmeticBinaryExpression.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/ArithmeticBinaryExpression.java similarity index 94% rename from server/src/main/java/org/apache/iotdb/db/query/expression/binary/ArithmeticBinaryExpression.java rename to server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/ArithmeticBinaryExpression.java index 185a8ce617de..3fdd5659a9ae 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/ArithmeticBinaryExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/ArithmeticBinaryExpression.java @@ -17,10 +17,10 @@ * under the License. */ -package org.apache.iotdb.db.query.expression.binary; +package org.apache.iotdb.db.mpp.plan.expression.binary; import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; -import org.apache.iotdb.db.query.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.nio.ByteBuffer; diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/BinaryExpression.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/BinaryExpression.java similarity index 92% rename from server/src/main/java/org/apache/iotdb/db/query/expression/binary/BinaryExpression.java rename to server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/BinaryExpression.java index 6dac04bf2983..218b67af8286 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/BinaryExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/BinaryExpression.java @@ -17,25 +17,25 @@ * under the License. */ -package org.apache.iotdb.db.query.expression.binary; +package org.apache.iotdb.db.mpp.plan.expression.binary; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.exception.query.LogicalOptimizeException; import org.apache.iotdb.db.exception.query.QueryProcessException; import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; +import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.InputLocation; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.dag.input.QueryDataSetInputLayer; +import org.apache.iotdb.db.mpp.transformation.dag.intermediate.IntermediateLayer; +import org.apache.iotdb.db.mpp.transformation.dag.intermediate.SingleInputColumnMultiReferenceIntermediateLayer; +import org.apache.iotdb.db.mpp.transformation.dag.intermediate.SingleInputColumnSingleReferenceIntermediateLayer; +import org.apache.iotdb.db.mpp.transformation.dag.memory.LayerMemoryAssigner; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.Transformer; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.binary.BinaryTransformer; +import org.apache.iotdb.db.mpp.transformation.dag.udf.UDTFContext; +import org.apache.iotdb.db.mpp.transformation.dag.udf.UDTFExecutor; import org.apache.iotdb.db.qp.physical.crud.UDTFPlan; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.udf.core.executor.UDTFContext; -import org.apache.iotdb.db.query.udf.core.executor.UDTFExecutor; -import org.apache.iotdb.db.query.udf.core.layer.IntermediateLayer; -import org.apache.iotdb.db.query.udf.core.layer.LayerMemoryAssigner; -import org.apache.iotdb.db.query.udf.core.layer.RawQueryInputLayer; -import org.apache.iotdb.db.query.udf.core.layer.SingleInputColumnMultiReferenceIntermediateLayer; -import org.apache.iotdb.db.query.udf.core.layer.SingleInputColumnSingleReferenceIntermediateLayer; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; -import org.apache.iotdb.db.query.udf.core.transformer.Transformer; -import org.apache.iotdb.db.query.udf.core.transformer.binary.BinaryTransformer; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.io.IOException; @@ -222,7 +222,7 @@ public void updateStatisticsForMemoryAssigner(LayerMemoryAssigner memoryAssigner public IntermediateLayer constructIntermediateLayer( long queryId, UDTFContext udtfContext, - RawQueryInputLayer rawTimeSeriesInputLayer, + QueryDataSetInputLayer rawTimeSeriesInputLayer, Map expressionIntermediateLayerMap, Map expressionDataTypeMap, LayerMemoryAssigner memoryAssigner) @@ -271,7 +271,7 @@ public IntermediateLayer constructIntermediateLayer( public IntermediateLayer constructIntermediateLayer( long queryId, UDTFContext udtfContext, - RawQueryInputLayer rawTimeSeriesInputLayer, + QueryDataSetInputLayer rawTimeSeriesInputLayer, Map expressionIntermediateLayerMap, TypeProvider typeProvider, LayerMemoryAssigner memoryAssigner) diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/CompareBinaryExpression.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/CompareBinaryExpression.java similarity index 96% rename from server/src/main/java/org/apache/iotdb/db/query/expression/binary/CompareBinaryExpression.java rename to server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/CompareBinaryExpression.java index 66f795faa1c0..5ea63fe7d100 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/CompareBinaryExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/CompareBinaryExpression.java @@ -17,10 +17,10 @@ * under the License. */ -package org.apache.iotdb.db.query.expression.binary; +package org.apache.iotdb.db.mpp.plan.expression.binary; import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; -import org.apache.iotdb.db.query.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.nio.ByteBuffer; diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/DivisionExpression.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/DivisionExpression.java similarity index 78% rename from server/src/main/java/org/apache/iotdb/db/query/expression/binary/DivisionExpression.java rename to server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/DivisionExpression.java index 8eb9308d9148..26ecfdb07189 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/DivisionExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/DivisionExpression.java @@ -17,13 +17,13 @@ * under the License. */ -package org.apache.iotdb.db.query.expression.binary; +package org.apache.iotdb.db.mpp.plan.expression.binary; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.ExpressionType; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; -import org.apache.iotdb.db.query.udf.core.transformer.binary.ArithmeticBinaryTransformer; -import org.apache.iotdb.db.query.udf.core.transformer.binary.ArithmeticDivisionTransformer; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ExpressionType; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.binary.ArithmeticBinaryTransformer; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.binary.ArithmeticDivisionTransformer; import java.nio.ByteBuffer; diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/EqualToExpression.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/EqualToExpression.java similarity index 78% rename from server/src/main/java/org/apache/iotdb/db/query/expression/binary/EqualToExpression.java rename to server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/EqualToExpression.java index d9662844023d..a9a683ce872c 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/EqualToExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/EqualToExpression.java @@ -17,13 +17,13 @@ * under the License. */ -package org.apache.iotdb.db.query.expression.binary; +package org.apache.iotdb.db.mpp.plan.expression.binary; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.ExpressionType; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; -import org.apache.iotdb.db.query.udf.core.transformer.binary.CompareBinaryTransformer; -import org.apache.iotdb.db.query.udf.core.transformer.binary.CompareEqualToTransformer; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ExpressionType; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.binary.CompareBinaryTransformer; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.binary.CompareEqualToTransformer; import java.nio.ByteBuffer; diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/GreaterEqualExpression.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/GreaterEqualExpression.java similarity index 78% rename from server/src/main/java/org/apache/iotdb/db/query/expression/binary/GreaterEqualExpression.java rename to server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/GreaterEqualExpression.java index 6d06a29284e7..d87a42046ff6 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/GreaterEqualExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/GreaterEqualExpression.java @@ -17,13 +17,13 @@ * under the License. */ -package org.apache.iotdb.db.query.expression.binary; +package org.apache.iotdb.db.mpp.plan.expression.binary; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.ExpressionType; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; -import org.apache.iotdb.db.query.udf.core.transformer.binary.CompareBinaryTransformer; -import org.apache.iotdb.db.query.udf.core.transformer.binary.CompareGreaterEqualTransformer; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ExpressionType; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.binary.CompareBinaryTransformer; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.binary.CompareGreaterEqualTransformer; import java.nio.ByteBuffer; diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/GreaterThanExpression.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/GreaterThanExpression.java similarity index 78% rename from server/src/main/java/org/apache/iotdb/db/query/expression/binary/GreaterThanExpression.java rename to server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/GreaterThanExpression.java index 6a574f68e98a..848557695cb9 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/GreaterThanExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/GreaterThanExpression.java @@ -17,13 +17,13 @@ * under the License. */ -package org.apache.iotdb.db.query.expression.binary; +package org.apache.iotdb.db.mpp.plan.expression.binary; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.ExpressionType; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; -import org.apache.iotdb.db.query.udf.core.transformer.binary.CompareBinaryTransformer; -import org.apache.iotdb.db.query.udf.core.transformer.binary.CompareGreaterThanTransformer; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ExpressionType; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.binary.CompareBinaryTransformer; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.binary.CompareGreaterThanTransformer; import java.nio.ByteBuffer; diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/LessEqualExpression.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/LessEqualExpression.java similarity index 78% rename from server/src/main/java/org/apache/iotdb/db/query/expression/binary/LessEqualExpression.java rename to server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/LessEqualExpression.java index a0fff83b49ee..36c9e122948c 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/LessEqualExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/LessEqualExpression.java @@ -17,13 +17,13 @@ * under the License. */ -package org.apache.iotdb.db.query.expression.binary; +package org.apache.iotdb.db.mpp.plan.expression.binary; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.ExpressionType; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; -import org.apache.iotdb.db.query.udf.core.transformer.binary.CompareBinaryTransformer; -import org.apache.iotdb.db.query.udf.core.transformer.binary.CompareLessEqualTransformer; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ExpressionType; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.binary.CompareBinaryTransformer; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.binary.CompareLessEqualTransformer; import java.nio.ByteBuffer; diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/LessThanExpression.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/LessThanExpression.java similarity index 78% rename from server/src/main/java/org/apache/iotdb/db/query/expression/binary/LessThanExpression.java rename to server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/LessThanExpression.java index 8f4978039248..630b7a4edcad 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/LessThanExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/LessThanExpression.java @@ -17,13 +17,13 @@ * under the License. */ -package org.apache.iotdb.db.query.expression.binary; +package org.apache.iotdb.db.mpp.plan.expression.binary; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.ExpressionType; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; -import org.apache.iotdb.db.query.udf.core.transformer.binary.CompareBinaryTransformer; -import org.apache.iotdb.db.query.udf.core.transformer.binary.CompareLessThanTransformer; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ExpressionType; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.binary.CompareBinaryTransformer; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.binary.CompareLessThanTransformer; import java.nio.ByteBuffer; diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/LogicAndExpression.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/LogicAndExpression.java similarity index 78% rename from server/src/main/java/org/apache/iotdb/db/query/expression/binary/LogicAndExpression.java rename to server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/LogicAndExpression.java index 26ac76b3c240..68addddc4942 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/LogicAndExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/LogicAndExpression.java @@ -17,13 +17,13 @@ * under the License. */ -package org.apache.iotdb.db.query.expression.binary; +package org.apache.iotdb.db.mpp.plan.expression.binary; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.ExpressionType; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; -import org.apache.iotdb.db.query.udf.core.transformer.binary.LogicAndTransformer; -import org.apache.iotdb.db.query.udf.core.transformer.binary.LogicBinaryTransformer; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ExpressionType; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.binary.LogicAndTransformer; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.binary.LogicBinaryTransformer; import java.nio.ByteBuffer; diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/LogicBinaryExpression.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/LogicBinaryExpression.java similarity index 94% rename from server/src/main/java/org/apache/iotdb/db/query/expression/binary/LogicBinaryExpression.java rename to server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/LogicBinaryExpression.java index 9ad7e20bfa2d..b4d644f68138 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/LogicBinaryExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/LogicBinaryExpression.java @@ -17,10 +17,10 @@ * under the License. */ -package org.apache.iotdb.db.query.expression.binary; +package org.apache.iotdb.db.mpp.plan.expression.binary; import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; -import org.apache.iotdb.db.query.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.nio.ByteBuffer; diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/LogicOrExpression.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/LogicOrExpression.java similarity index 78% rename from server/src/main/java/org/apache/iotdb/db/query/expression/binary/LogicOrExpression.java rename to server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/LogicOrExpression.java index eff1d563578b..785263b2104e 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/LogicOrExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/LogicOrExpression.java @@ -17,13 +17,13 @@ * under the License. */ -package org.apache.iotdb.db.query.expression.binary; +package org.apache.iotdb.db.mpp.plan.expression.binary; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.ExpressionType; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; -import org.apache.iotdb.db.query.udf.core.transformer.binary.LogicBinaryTransformer; -import org.apache.iotdb.db.query.udf.core.transformer.binary.LogicOrTransformer; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ExpressionType; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.binary.LogicBinaryTransformer; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.binary.LogicOrTransformer; import java.nio.ByteBuffer; diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/ModuloExpression.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/ModuloExpression.java similarity index 77% rename from server/src/main/java/org/apache/iotdb/db/query/expression/binary/ModuloExpression.java rename to server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/ModuloExpression.java index 39f4738acac3..24f7ab6b40c3 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/ModuloExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/ModuloExpression.java @@ -17,13 +17,13 @@ * under the License. */ -package org.apache.iotdb.db.query.expression.binary; +package org.apache.iotdb.db.mpp.plan.expression.binary; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.ExpressionType; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; -import org.apache.iotdb.db.query.udf.core.transformer.binary.ArithmeticBinaryTransformer; -import org.apache.iotdb.db.query.udf.core.transformer.binary.ArithmeticModuloTransformer; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ExpressionType; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.binary.ArithmeticBinaryTransformer; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.binary.ArithmeticModuloTransformer; import java.nio.ByteBuffer; diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/MultiplicationExpression.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/MultiplicationExpression.java similarity index 78% rename from server/src/main/java/org/apache/iotdb/db/query/expression/binary/MultiplicationExpression.java rename to server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/MultiplicationExpression.java index 6b72d5a2d275..adc381612d6b 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/MultiplicationExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/MultiplicationExpression.java @@ -17,13 +17,13 @@ * under the License. */ -package org.apache.iotdb.db.query.expression.binary; +package org.apache.iotdb.db.mpp.plan.expression.binary; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.ExpressionType; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; -import org.apache.iotdb.db.query.udf.core.transformer.binary.ArithmeticBinaryTransformer; -import org.apache.iotdb.db.query.udf.core.transformer.binary.ArithmeticMultiplicationTransformer; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ExpressionType; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.binary.ArithmeticBinaryTransformer; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.binary.ArithmeticMultiplicationTransformer; import java.nio.ByteBuffer; diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/NonEqualExpression.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/NonEqualExpression.java similarity index 78% rename from server/src/main/java/org/apache/iotdb/db/query/expression/binary/NonEqualExpression.java rename to server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/NonEqualExpression.java index 542d4a7c2559..2a25f392f291 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/NonEqualExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/NonEqualExpression.java @@ -17,13 +17,13 @@ * under the License. */ -package org.apache.iotdb.db.query.expression.binary; +package org.apache.iotdb.db.mpp.plan.expression.binary; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.ExpressionType; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; -import org.apache.iotdb.db.query.udf.core.transformer.binary.CompareBinaryTransformer; -import org.apache.iotdb.db.query.udf.core.transformer.binary.CompareNonEqualTransformer; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ExpressionType; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.binary.CompareBinaryTransformer; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.binary.CompareNonEqualTransformer; import java.nio.ByteBuffer; diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/SubtractionExpression.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/SubtractionExpression.java similarity index 78% rename from server/src/main/java/org/apache/iotdb/db/query/expression/binary/SubtractionExpression.java rename to server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/SubtractionExpression.java index 372d56cc73a0..946179f7820e 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/binary/SubtractionExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/binary/SubtractionExpression.java @@ -17,13 +17,13 @@ * under the License. */ -package org.apache.iotdb.db.query.expression.binary; +package org.apache.iotdb.db.mpp.plan.expression.binary; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.ExpressionType; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; -import org.apache.iotdb.db.query.udf.core.transformer.binary.ArithmeticBinaryTransformer; -import org.apache.iotdb.db.query.udf.core.transformer.binary.ArithmeticSubtractionTransformer; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ExpressionType; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.binary.ArithmeticBinaryTransformer; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.binary.ArithmeticSubtractionTransformer; import java.nio.ByteBuffer; diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/leaf/ConstantOperand.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/leaf/ConstantOperand.java similarity index 87% rename from server/src/main/java/org/apache/iotdb/db/query/expression/leaf/ConstantOperand.java rename to server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/leaf/ConstantOperand.java index 8a6c4589c0d7..db142fc023da 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/leaf/ConstantOperand.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/leaf/ConstantOperand.java @@ -17,20 +17,20 @@ * under the License. */ -package org.apache.iotdb.db.query.expression.leaf; +package org.apache.iotdb.db.mpp.plan.expression.leaf; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.exception.query.QueryProcessException; import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ExpressionType; import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.InputLocation; +import org.apache.iotdb.db.mpp.transformation.dag.input.QueryDataSetInputLayer; +import org.apache.iotdb.db.mpp.transformation.dag.intermediate.ConstantIntermediateLayer; +import org.apache.iotdb.db.mpp.transformation.dag.intermediate.IntermediateLayer; +import org.apache.iotdb.db.mpp.transformation.dag.memory.LayerMemoryAssigner; +import org.apache.iotdb.db.mpp.transformation.dag.udf.UDTFContext; import org.apache.iotdb.db.qp.physical.crud.UDTFPlan; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.ExpressionType; -import org.apache.iotdb.db.query.udf.core.executor.UDTFContext; -import org.apache.iotdb.db.query.udf.core.layer.ConstantIntermediateLayer; -import org.apache.iotdb.db.query.udf.core.layer.IntermediateLayer; -import org.apache.iotdb.db.query.udf.core.layer.LayerMemoryAssigner; -import org.apache.iotdb.db.query.udf.core.layer.RawQueryInputLayer; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils; @@ -120,7 +120,7 @@ public void updateStatisticsForMemoryAssigner(LayerMemoryAssigner memoryAssigner public IntermediateLayer constructIntermediateLayer( long queryId, UDTFContext udtfContext, - RawQueryInputLayer rawTimeSeriesInputLayer, + QueryDataSetInputLayer rawTimeSeriesInputLayer, Map expressionIntermediateLayerMap, Map expressionDataTypeMap, LayerMemoryAssigner memoryAssigner) @@ -139,7 +139,7 @@ public IntermediateLayer constructIntermediateLayer( public IntermediateLayer constructIntermediateLayer( long queryId, UDTFContext udtfContext, - RawQueryInputLayer rawTimeSeriesInputLayer, + QueryDataSetInputLayer rawTimeSeriesInputLayer, Map expressionIntermediateLayerMap, TypeProvider typeProvider, LayerMemoryAssigner memoryAssigner) diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/leaf/LeafOperand.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/leaf/LeafOperand.java similarity index 87% rename from server/src/main/java/org/apache/iotdb/db/query/expression/leaf/LeafOperand.java rename to server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/leaf/LeafOperand.java index 500ae8952000..9f4bc80438a1 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/leaf/LeafOperand.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/leaf/LeafOperand.java @@ -17,10 +17,10 @@ * under the License. */ -package org.apache.iotdb.db.query.expression.leaf; +package org.apache.iotdb.db.mpp.plan.expression.leaf; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.udf.core.executor.UDTFExecutor; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.transformation.dag.udf.UDTFExecutor; import java.time.ZoneId; import java.util.Collections; diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/leaf/TimeSeriesOperand.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/leaf/TimeSeriesOperand.java similarity index 86% rename from server/src/main/java/org/apache/iotdb/db/query/expression/leaf/TimeSeriesOperand.java rename to server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/leaf/TimeSeriesOperand.java index cdab2e4adc53..93266720482a 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/leaf/TimeSeriesOperand.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/leaf/TimeSeriesOperand.java @@ -17,24 +17,24 @@ * under the License. */ -package org.apache.iotdb.db.query.expression.leaf; +package org.apache.iotdb.db.mpp.plan.expression.leaf; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.exception.query.LogicalOptimizeException; import org.apache.iotdb.db.exception.query.QueryProcessException; import org.apache.iotdb.db.metadata.path.PathDeserializeUtil; import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ExpressionType; import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.InputLocation; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.dag.input.QueryDataSetInputLayer; +import org.apache.iotdb.db.mpp.transformation.dag.intermediate.IntermediateLayer; +import org.apache.iotdb.db.mpp.transformation.dag.intermediate.SingleInputColumnMultiReferenceIntermediateLayer; +import org.apache.iotdb.db.mpp.transformation.dag.intermediate.SingleInputColumnSingleReferenceIntermediateLayer; +import org.apache.iotdb.db.mpp.transformation.dag.memory.LayerMemoryAssigner; +import org.apache.iotdb.db.mpp.transformation.dag.udf.UDTFContext; import org.apache.iotdb.db.qp.physical.crud.UDTFPlan; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.ExpressionType; -import org.apache.iotdb.db.query.udf.core.executor.UDTFContext; -import org.apache.iotdb.db.query.udf.core.layer.IntermediateLayer; -import org.apache.iotdb.db.query.udf.core.layer.LayerMemoryAssigner; -import org.apache.iotdb.db.query.udf.core.layer.RawQueryInputLayer; -import org.apache.iotdb.db.query.udf.core.layer.SingleInputColumnMultiReferenceIntermediateLayer; -import org.apache.iotdb.db.query.udf.core.layer.SingleInputColumnSingleReferenceIntermediateLayer; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.io.IOException; @@ -118,7 +118,7 @@ public void updateStatisticsForMemoryAssigner(LayerMemoryAssigner memoryAssigner public IntermediateLayer constructIntermediateLayer( long queryId, UDTFContext udtfContext, - RawQueryInputLayer rawTimeSeriesInputLayer, + QueryDataSetInputLayer rawTimeSeriesInputLayer, Map expressionIntermediateLayerMap, Map expressionDataTypeMap, LayerMemoryAssigner memoryAssigner) @@ -146,7 +146,7 @@ public IntermediateLayer constructIntermediateLayer( public IntermediateLayer constructIntermediateLayer( long queryId, UDTFContext udtfContext, - RawQueryInputLayer rawTimeSeriesInputLayer, + QueryDataSetInputLayer rawTimeSeriesInputLayer, Map expressionIntermediateLayerMap, TypeProvider typeProvider, LayerMemoryAssigner memoryAssigner) diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/leaf/TimestampOperand.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/leaf/TimestampOperand.java similarity index 85% rename from server/src/main/java/org/apache/iotdb/db/query/expression/leaf/TimestampOperand.java rename to server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/leaf/TimestampOperand.java index 8c9f6c741df9..fec495d53e66 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/leaf/TimestampOperand.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/leaf/TimestampOperand.java @@ -17,23 +17,23 @@ * under the License. */ -package org.apache.iotdb.db.query.expression.leaf; +package org.apache.iotdb.db.mpp.plan.expression.leaf; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.exception.query.LogicalOptimizeException; import org.apache.iotdb.db.exception.query.QueryProcessException; import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ExpressionType; import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.InputLocation; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.dag.input.QueryDataSetInputLayer; +import org.apache.iotdb.db.mpp.transformation.dag.intermediate.IntermediateLayer; +import org.apache.iotdb.db.mpp.transformation.dag.intermediate.SingleInputColumnMultiReferenceIntermediateLayer; +import org.apache.iotdb.db.mpp.transformation.dag.intermediate.SingleInputColumnSingleReferenceIntermediateLayer; +import org.apache.iotdb.db.mpp.transformation.dag.memory.LayerMemoryAssigner; +import org.apache.iotdb.db.mpp.transformation.dag.udf.UDTFContext; import org.apache.iotdb.db.qp.physical.crud.UDTFPlan; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.ExpressionType; -import org.apache.iotdb.db.query.udf.core.executor.UDTFContext; -import org.apache.iotdb.db.query.udf.core.layer.IntermediateLayer; -import org.apache.iotdb.db.query.udf.core.layer.LayerMemoryAssigner; -import org.apache.iotdb.db.query.udf.core.layer.RawQueryInputLayer; -import org.apache.iotdb.db.query.udf.core.layer.SingleInputColumnMultiReferenceIntermediateLayer; -import org.apache.iotdb.db.query.udf.core.layer.SingleInputColumnSingleReferenceIntermediateLayer; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.io.IOException; @@ -103,7 +103,7 @@ public void updateStatisticsForMemoryAssigner(LayerMemoryAssigner memoryAssigner public IntermediateLayer constructIntermediateLayer( long queryId, UDTFContext udtfContext, - RawQueryInputLayer rawTimeSeriesInputLayer, + QueryDataSetInputLayer rawTimeSeriesInputLayer, Map expressionIntermediateLayerMap, Map expressionDataTypeMap, LayerMemoryAssigner memoryAssigner) @@ -130,7 +130,7 @@ public IntermediateLayer constructIntermediateLayer( public IntermediateLayer constructIntermediateLayer( long queryId, UDTFContext udtfContext, - RawQueryInputLayer rawTimeSeriesInputLayer, + QueryDataSetInputLayer rawTimeSeriesInputLayer, Map expressionIntermediateLayerMap, TypeProvider typeProvider, LayerMemoryAssigner memoryAssigner) diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/multi/FunctionExpression.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/multi/FunctionExpression.java similarity index 87% rename from server/src/main/java/org/apache/iotdb/db/query/expression/multi/FunctionExpression.java rename to server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/multi/FunctionExpression.java index caa87859e6a4..8dbe67c10d46 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/multi/FunctionExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/multi/FunctionExpression.java @@ -17,36 +17,36 @@ * under the License. */ -package org.apache.iotdb.db.query.expression.multi; +package org.apache.iotdb.db.mpp.plan.expression.multi; import org.apache.iotdb.commons.conf.IoTDBConstant; import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.commons.udf.api.customizer.strategy.AccessStrategy; import org.apache.iotdb.db.exception.query.LogicalOptimizeException; import org.apache.iotdb.db.exception.query.QueryProcessException; import org.apache.iotdb.db.exception.sql.SemanticException; import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ExpressionType; +import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand; import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.InputLocation; +import org.apache.iotdb.db.mpp.transformation.dag.input.QueryDataSetInputLayer; +import org.apache.iotdb.db.mpp.transformation.dag.intermediate.IntermediateLayer; +import org.apache.iotdb.db.mpp.transformation.dag.intermediate.MultiInputColumnIntermediateLayer; +import org.apache.iotdb.db.mpp.transformation.dag.intermediate.SingleInputColumnMultiReferenceIntermediateLayer; +import org.apache.iotdb.db.mpp.transformation.dag.intermediate.SingleInputColumnSingleReferenceIntermediateLayer; +import org.apache.iotdb.db.mpp.transformation.dag.memory.LayerMemoryAssigner; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.Transformer; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.multi.UDFQueryRowTransformer; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.multi.UDFQueryRowWindowTransformer; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.multi.UDFQueryTransformer; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.unary.TransparentTransformer; +import org.apache.iotdb.db.mpp.transformation.dag.udf.UDTFContext; +import org.apache.iotdb.db.mpp.transformation.dag.udf.UDTFExecutor; +import org.apache.iotdb.db.mpp.transformation.dag.udf.UDTFTypeInferrer; import org.apache.iotdb.db.qp.constant.SQLConstant; import org.apache.iotdb.db.qp.physical.crud.UDTFPlan; import org.apache.iotdb.db.qp.strategy.optimizer.ConcatPathOptimizer; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.ExpressionType; -import org.apache.iotdb.db.query.expression.leaf.TimeSeriesOperand; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.AccessStrategy; -import org.apache.iotdb.db.query.udf.core.executor.UDTFContext; -import org.apache.iotdb.db.query.udf.core.executor.UDTFExecutor; -import org.apache.iotdb.db.query.udf.core.executor.UDTFTypeInferrer; -import org.apache.iotdb.db.query.udf.core.layer.IntermediateLayer; -import org.apache.iotdb.db.query.udf.core.layer.LayerMemoryAssigner; -import org.apache.iotdb.db.query.udf.core.layer.MultiInputColumnIntermediateLayer; -import org.apache.iotdb.db.query.udf.core.layer.RawQueryInputLayer; -import org.apache.iotdb.db.query.udf.core.layer.SingleInputColumnMultiReferenceIntermediateLayer; -import org.apache.iotdb.db.query.udf.core.layer.SingleInputColumnSingleReferenceIntermediateLayer; -import org.apache.iotdb.db.query.udf.core.transformer.Transformer; -import org.apache.iotdb.db.query.udf.core.transformer.multi.UDFQueryRowTransformer; -import org.apache.iotdb.db.query.udf.core.transformer.multi.UDFQueryRowWindowTransformer; -import org.apache.iotdb.db.query.udf.core.transformer.multi.UDFQueryTransformer; -import org.apache.iotdb.db.query.udf.core.transformer.unary.TransparentTransformer; import org.apache.iotdb.db.utils.TypeInferenceUtils; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils; @@ -240,7 +240,7 @@ public void constructUdfExecutors( for (Expression expression : expressions) { expression.constructUdfExecutors(expressionName2Executor, zoneId); } - expressionName2Executor.put(expressionString, new UDTFExecutor(this, zoneId)); + expressionName2Executor.put(expressionString, new UDTFExecutor(functionName, zoneId)); } @Override @@ -254,7 +254,15 @@ public TSDataType inferTypes(TypeProvider typeProvider) { if (isTimeSeriesGeneratingFunctionExpression()) { typeProvider.setType( - expressionString, new UDTFTypeInferrer(this).inferOutputType(typeProvider)); + expressionString, + new UDTFTypeInferrer(functionName) + .inferOutputType( + expressions.stream().map(Expression::toString).collect(Collectors.toList()), + getPaths(), + expressions.stream() + .map(f -> typeProvider.getType(f.toString())) + .collect(Collectors.toList()), + functionAttributes)); } else { if (expressions.size() != 1) { throw new SemanticException( @@ -305,7 +313,7 @@ public void updateStatisticsForMemoryAssigner(LayerMemoryAssigner memoryAssigner public IntermediateLayer constructIntermediateLayer( long queryId, UDTFContext udtfContext, - RawQueryInputLayer rawTimeSeriesInputLayer, + QueryDataSetInputLayer rawTimeSeriesInputLayer, Map expressionIntermediateLayerMap, TypeProvider typeProvider, LayerMemoryAssigner memoryAssigner) @@ -345,7 +353,7 @@ public IntermediateLayer constructIntermediateLayer( private IntermediateLayer constructUdfInputIntermediateLayer( long queryId, UDTFContext udtfContext, - RawQueryInputLayer rawTimeSeriesInputLayer, + QueryDataSetInputLayer rawTimeSeriesInputLayer, Map expressionIntermediateLayerMap, TypeProvider typeProvider, LayerMemoryAssigner memoryAssigner) @@ -381,7 +389,15 @@ private UDFQueryTransformer constructUdfTransformer( throws QueryProcessException, IOException { UDTFExecutor executor = udtfContext.getExecutorByFunctionExpression(this); - executor.beforeStart(queryId, memoryAssigner.assign(), typeProvider); + executor.beforeStart( + queryId, + memoryAssigner.assign(), + expressions.stream().map(Expression::toString).collect(Collectors.toList()), + getPaths(), + expressions.stream() + .map(f -> typeProvider.getType(f.toString())) + .collect(Collectors.toList()), + functionAttributes); AccessStrategy accessStrategy = executor.getConfigurations().getAccessStrategy(); switch (accessStrategy.getAccessStrategyType()) { @@ -402,7 +418,7 @@ private UDFQueryTransformer constructUdfTransformer( public IntermediateLayer constructIntermediateLayer( long queryId, UDTFContext udtfContext, - RawQueryInputLayer rawTimeSeriesInputLayer, + QueryDataSetInputLayer rawTimeSeriesInputLayer, Map expressionIntermediateLayerMap, Map expressionDataTypeMap, LayerMemoryAssigner memoryAssigner) @@ -449,7 +465,7 @@ public IntermediateLayer constructIntermediateLayer( private IntermediateLayer constructUdfInputIntermediateLayer( long queryId, UDTFContext udtfContext, - RawQueryInputLayer rawTimeSeriesInputLayer, + QueryDataSetInputLayer rawTimeSeriesInputLayer, Map expressionIntermediateLayerMap, Map expressionDataTypeMap, LayerMemoryAssigner memoryAssigner) @@ -487,7 +503,13 @@ private UDFQueryTransformer constructUdfTransformer( throws QueryProcessException, IOException { UDTFExecutor executor = udtfContext.getExecutorByFunctionExpression(this); - executor.beforeStart(queryId, memoryAssigner.assign(), expressionDataTypeMap); + executor.beforeStart( + queryId, + memoryAssigner.assign(), + expressions.stream().map(Expression::toString).collect(Collectors.toList()), + getPaths(), + expressions.stream().map(expressionDataTypeMap::get).collect(Collectors.toList()), + functionAttributes); AccessStrategy accessStrategy = executor.getConfigurations().getAccessStrategy(); switch (accessStrategy.getAccessStrategyType()) { diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/unary/InExpression.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/unary/InExpression.java similarity index 89% rename from server/src/main/java/org/apache/iotdb/db/query/expression/unary/InExpression.java rename to server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/unary/InExpression.java index 979d4c2bbc91..28e5c3307df1 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/unary/InExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/unary/InExpression.java @@ -17,14 +17,14 @@ * under the License. */ -package org.apache.iotdb.db.query.expression.unary; +package org.apache.iotdb.db.mpp.plan.expression.unary; import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.ExpressionType; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; -import org.apache.iotdb.db.query.udf.core.transformer.Transformer; -import org.apache.iotdb.db.query.udf.core.transformer.unary.InTransformer; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ExpressionType; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.Transformer; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.unary.InTransformer; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils; diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/unary/LikeExpression.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/unary/LikeExpression.java similarity index 91% rename from server/src/main/java/org/apache/iotdb/db/query/expression/unary/LikeExpression.java rename to server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/unary/LikeExpression.java index 0e94037e721c..1c9164240b78 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/unary/LikeExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/unary/LikeExpression.java @@ -17,15 +17,15 @@ * under the License. */ -package org.apache.iotdb.db.query.expression.unary; +package org.apache.iotdb.db.mpp.plan.expression.unary; import org.apache.iotdb.db.exception.sql.SemanticException; import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.ExpressionType; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; -import org.apache.iotdb.db.query.udf.core.transformer.Transformer; -import org.apache.iotdb.db.query.udf.core.transformer.unary.RegularTransformer; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ExpressionType; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.Transformer; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.unary.RegularTransformer; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils; @@ -122,7 +122,7 @@ private String unescapeString(String value) { public TSDataType inferTypes(TypeProvider typeProvider) throws SemanticException { final String expressionString = toString(); if (!typeProvider.containsTypeInfoOf(expressionString)) { - checkInputExpressionDataType( + Expression.checkInputExpressionDataType( expression.toString(), expression.inferTypes(typeProvider), TSDataType.TEXT); typeProvider.setType(expressionString, TSDataType.TEXT); } diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/unary/LogicNotExpression.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/unary/LogicNotExpression.java similarity index 77% rename from server/src/main/java/org/apache/iotdb/db/query/expression/unary/LogicNotExpression.java rename to server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/unary/LogicNotExpression.java index a538e4a02833..9a7191d49757 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/unary/LogicNotExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/unary/LogicNotExpression.java @@ -17,18 +17,18 @@ * under the License. */ -package org.apache.iotdb.db.query.expression.unary; +package org.apache.iotdb.db.mpp.plan.expression.unary; import org.apache.iotdb.db.exception.sql.SemanticException; import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.ExpressionType; -import org.apache.iotdb.db.query.expression.leaf.ConstantOperand; -import org.apache.iotdb.db.query.expression.leaf.TimeSeriesOperand; -import org.apache.iotdb.db.query.expression.multi.FunctionExpression; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; -import org.apache.iotdb.db.query.udf.core.transformer.Transformer; -import org.apache.iotdb.db.query.udf.core.transformer.unary.LogicNotTransformer; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ExpressionType; +import org.apache.iotdb.db.mpp.plan.expression.leaf.ConstantOperand; +import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand; +import org.apache.iotdb.db.mpp.plan.expression.multi.FunctionExpression; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.Transformer; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.unary.LogicNotTransformer; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.nio.ByteBuffer; @@ -57,7 +57,7 @@ protected Expression constructExpression(Expression childExpression) { public TSDataType inferTypes(TypeProvider typeProvider) throws SemanticException { final String expressionString = toString(); if (!typeProvider.containsTypeInfoOf(expressionString)) { - checkInputExpressionDataType( + Expression.checkInputExpressionDataType( expression.toString(), expression.inferTypes(typeProvider), TSDataType.BOOLEAN); typeProvider.setType(expressionString, TSDataType.BOOLEAN); } diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/unary/NegationExpression.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/unary/NegationExpression.java similarity index 79% rename from server/src/main/java/org/apache/iotdb/db/query/expression/unary/NegationExpression.java rename to server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/unary/NegationExpression.java index 6d8c11107821..2b9cba69b810 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/unary/NegationExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/unary/NegationExpression.java @@ -17,18 +17,18 @@ * under the License. */ -package org.apache.iotdb.db.query.expression.unary; +package org.apache.iotdb.db.mpp.plan.expression.unary; import org.apache.iotdb.db.exception.sql.SemanticException; import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.ExpressionType; -import org.apache.iotdb.db.query.expression.leaf.ConstantOperand; -import org.apache.iotdb.db.query.expression.leaf.TimeSeriesOperand; -import org.apache.iotdb.db.query.expression.multi.FunctionExpression; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; -import org.apache.iotdb.db.query.udf.core.transformer.Transformer; -import org.apache.iotdb.db.query.udf.core.transformer.unary.ArithmeticNegationTransformer; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ExpressionType; +import org.apache.iotdb.db.mpp.plan.expression.leaf.ConstantOperand; +import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand; +import org.apache.iotdb.db.mpp.plan.expression.multi.FunctionExpression; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.Transformer; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.unary.ArithmeticNegationTransformer; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.nio.ByteBuffer; @@ -58,7 +58,7 @@ public TSDataType inferTypes(TypeProvider typeProvider) throws SemanticException final String expressionString = toString(); if (!typeProvider.containsTypeInfoOf(expressionString)) { TSDataType inputExpressionType = expression.inferTypes(typeProvider); - checkInputExpressionDataType( + Expression.checkInputExpressionDataType( expression.toString(), inputExpressionType, TSDataType.INT32, diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/unary/RegularExpression.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/unary/RegularExpression.java similarity index 88% rename from server/src/main/java/org/apache/iotdb/db/query/expression/unary/RegularExpression.java rename to server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/unary/RegularExpression.java index a8b30f0d5af1..8d8b1e11866d 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/unary/RegularExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/unary/RegularExpression.java @@ -17,15 +17,15 @@ * under the License. */ -package org.apache.iotdb.db.query.expression.unary; +package org.apache.iotdb.db.mpp.plan.expression.unary; import org.apache.iotdb.db.exception.sql.SemanticException; import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.ExpressionType; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; -import org.apache.iotdb.db.query.udf.core.transformer.Transformer; -import org.apache.iotdb.db.query.udf.core.transformer.unary.RegularTransformer; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ExpressionType; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.Transformer; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.unary.RegularTransformer; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils; diff --git a/server/src/main/java/org/apache/iotdb/db/query/expression/unary/UnaryExpression.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/unary/UnaryExpression.java similarity index 89% rename from server/src/main/java/org/apache/iotdb/db/query/expression/unary/UnaryExpression.java rename to server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/unary/UnaryExpression.java index 02ec49a4b129..8370eba56ad5 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/expression/unary/UnaryExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/unary/UnaryExpression.java @@ -17,25 +17,25 @@ * under the License. */ -package org.apache.iotdb.db.query.expression.unary; +package org.apache.iotdb.db.mpp.plan.expression.unary; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.exception.query.LogicalOptimizeException; import org.apache.iotdb.db.exception.query.QueryProcessException; import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; +import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.InputLocation; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.dag.input.QueryDataSetInputLayer; +import org.apache.iotdb.db.mpp.transformation.dag.intermediate.IntermediateLayer; +import org.apache.iotdb.db.mpp.transformation.dag.intermediate.SingleInputColumnMultiReferenceIntermediateLayer; +import org.apache.iotdb.db.mpp.transformation.dag.intermediate.SingleInputColumnSingleReferenceIntermediateLayer; +import org.apache.iotdb.db.mpp.transformation.dag.memory.LayerMemoryAssigner; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.Transformer; +import org.apache.iotdb.db.mpp.transformation.dag.udf.UDTFContext; +import org.apache.iotdb.db.mpp.transformation.dag.udf.UDTFExecutor; import org.apache.iotdb.db.qp.physical.crud.UDTFPlan; import org.apache.iotdb.db.qp.utils.WildcardsRemover; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.udf.core.executor.UDTFContext; -import org.apache.iotdb.db.query.udf.core.executor.UDTFExecutor; -import org.apache.iotdb.db.query.udf.core.layer.IntermediateLayer; -import org.apache.iotdb.db.query.udf.core.layer.LayerMemoryAssigner; -import org.apache.iotdb.db.query.udf.core.layer.RawQueryInputLayer; -import org.apache.iotdb.db.query.udf.core.layer.SingleInputColumnMultiReferenceIntermediateLayer; -import org.apache.iotdb.db.query.udf.core.layer.SingleInputColumnSingleReferenceIntermediateLayer; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; -import org.apache.iotdb.db.query.udf.core.transformer.Transformer; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.io.IOException; @@ -118,7 +118,7 @@ public final void updateStatisticsForMemoryAssigner(LayerMemoryAssigner memoryAs public final IntermediateLayer constructIntermediateLayer( long queryId, UDTFContext udtfContext, - RawQueryInputLayer rawTimeSeriesInputLayer, + QueryDataSetInputLayer rawTimeSeriesInputLayer, Map expressionIntermediateLayerMap, Map expressionDataTypeMap, LayerMemoryAssigner memoryAssigner) @@ -156,7 +156,7 @@ public final IntermediateLayer constructIntermediateLayer( public IntermediateLayer constructIntermediateLayer( long queryId, UDTFContext udtfContext, - RawQueryInputLayer rawTimeSeriesInputLayer, + QueryDataSetInputLayer rawTimeSeriesInputLayer, Map expressionIntermediateLayerMap, TypeProvider typeProvider, LayerMemoryAssigner memoryAssigner) diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java index dab981d3ea18..ec6a293d5328 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java @@ -28,6 +28,29 @@ import org.apache.iotdb.db.mpp.common.filter.BasicFunctionFilter; import org.apache.iotdb.db.mpp.common.filter.QueryFilter; import org.apache.iotdb.db.mpp.plan.analyze.ExpressionAnalyzer; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.binary.AdditionExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.DivisionExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.EqualToExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.GreaterEqualExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.GreaterThanExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.LessEqualExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.LessThanExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.LogicAndExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.LogicOrExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.ModuloExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.MultiplicationExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.NonEqualExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.SubtractionExpression; +import org.apache.iotdb.db.mpp.plan.expression.leaf.ConstantOperand; +import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand; +import org.apache.iotdb.db.mpp.plan.expression.leaf.TimestampOperand; +import org.apache.iotdb.db.mpp.plan.expression.multi.FunctionExpression; +import org.apache.iotdb.db.mpp.plan.expression.unary.InExpression; +import org.apache.iotdb.db.mpp.plan.expression.unary.LikeExpression; +import org.apache.iotdb.db.mpp.plan.expression.unary.LogicNotExpression; +import org.apache.iotdb.db.mpp.plan.expression.unary.NegationExpression; +import org.apache.iotdb.db.mpp.plan.expression.unary.RegularExpression; import org.apache.iotdb.db.mpp.plan.statement.Statement; import org.apache.iotdb.db.mpp.plan.statement.component.FillComponent; import org.apache.iotdb.db.mpp.plan.statement.component.FillPolicy; @@ -78,29 +101,6 @@ import org.apache.iotdb.db.qp.sql.IoTDBSqlParser.ExpressionContext; import org.apache.iotdb.db.qp.sql.IoTDBSqlParserBaseVisitor; import org.apache.iotdb.db.qp.utils.DatetimeUtils; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.binary.AdditionExpression; -import org.apache.iotdb.db.query.expression.binary.DivisionExpression; -import org.apache.iotdb.db.query.expression.binary.EqualToExpression; -import org.apache.iotdb.db.query.expression.binary.GreaterEqualExpression; -import org.apache.iotdb.db.query.expression.binary.GreaterThanExpression; -import org.apache.iotdb.db.query.expression.binary.LessEqualExpression; -import org.apache.iotdb.db.query.expression.binary.LessThanExpression; -import org.apache.iotdb.db.query.expression.binary.LogicAndExpression; -import org.apache.iotdb.db.query.expression.binary.LogicOrExpression; -import org.apache.iotdb.db.query.expression.binary.ModuloExpression; -import org.apache.iotdb.db.query.expression.binary.MultiplicationExpression; -import org.apache.iotdb.db.query.expression.binary.NonEqualExpression; -import org.apache.iotdb.db.query.expression.binary.SubtractionExpression; -import org.apache.iotdb.db.query.expression.leaf.ConstantOperand; -import org.apache.iotdb.db.query.expression.leaf.TimeSeriesOperand; -import org.apache.iotdb.db.query.expression.leaf.TimestampOperand; -import org.apache.iotdb.db.query.expression.multi.FunctionExpression; -import org.apache.iotdb.db.query.expression.unary.InExpression; -import org.apache.iotdb.db.query.expression.unary.LikeExpression; -import org.apache.iotdb.db.query.expression.unary.LogicNotExpression; -import org.apache.iotdb.db.query.expression.unary.NegationExpression; -import org.apache.iotdb.db.query.expression.unary.RegularExpression; import org.apache.iotdb.tsfile.common.conf.TSFileDescriptor; import org.apache.iotdb.tsfile.common.constant.TsFileConstant; import org.apache.iotdb.tsfile.file.metadata.enums.CompressionType; diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/StatementGenerator.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/StatementGenerator.java index 6c3749afb023..7103246f38f2 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/StatementGenerator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/StatementGenerator.java @@ -24,6 +24,12 @@ import org.apache.iotdb.db.exception.query.QueryProcessException; import org.apache.iotdb.db.mpp.common.filter.BasicFunctionFilter; import org.apache.iotdb.db.mpp.plan.constant.FilterConstant; +import org.apache.iotdb.db.mpp.plan.expression.binary.GreaterEqualExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.LessThanExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.LogicAndExpression; +import org.apache.iotdb.db.mpp.plan.expression.leaf.ConstantOperand; +import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand; +import org.apache.iotdb.db.mpp.plan.expression.leaf.TimestampOperand; import org.apache.iotdb.db.mpp.plan.statement.Statement; import org.apache.iotdb.db.mpp.plan.statement.component.FromComponent; import org.apache.iotdb.db.mpp.plan.statement.component.ResultColumn; @@ -43,12 +49,6 @@ import org.apache.iotdb.db.qp.sql.IoTDBSqlParser; import org.apache.iotdb.db.qp.sql.SqlLexer; import org.apache.iotdb.db.qp.strategy.SQLParseError; -import org.apache.iotdb.db.query.expression.binary.GreaterEqualExpression; -import org.apache.iotdb.db.query.expression.binary.LessThanExpression; -import org.apache.iotdb.db.query.expression.binary.LogicAndExpression; -import org.apache.iotdb.db.query.expression.leaf.ConstantOperand; -import org.apache.iotdb.db.query.expression.leaf.TimeSeriesOperand; -import org.apache.iotdb.db.query.expression.leaf.TimestampOperand; import org.apache.iotdb.db.utils.QueryDataSetUtils; import org.apache.iotdb.service.rpc.thrift.TSCreateAlignedTimeseriesReq; import org.apache.iotdb.service.rpc.thrift.TSCreateMultiTimeseriesReq; diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LocalExecutionPlanner.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LocalExecutionPlanner.java index 6fae3156b083..34f18ad5e9f8 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LocalExecutionPlanner.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LocalExecutionPlanner.java @@ -93,6 +93,7 @@ import org.apache.iotdb.db.mpp.execution.operator.source.SeriesAggregationScanOperator; import org.apache.iotdb.db.mpp.execution.operator.source.SeriesScanOperator; import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; +import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanVisitor; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.ChildNodesSchemaScanNode; @@ -133,7 +134,6 @@ import org.apache.iotdb.db.mpp.plan.statement.component.FillPolicy; import org.apache.iotdb.db.mpp.plan.statement.component.OrderBy; import org.apache.iotdb.db.mpp.plan.statement.literal.Literal; -import org.apache.iotdb.db.query.expression.Expression; import org.apache.iotdb.db.utils.datastructure.TimeSelector; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.read.filter.basic.Filter; diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanBuilder.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanBuilder.java index 45dab8de2d86..589685a68766 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanBuilder.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanBuilder.java @@ -29,6 +29,9 @@ import org.apache.iotdb.db.mpp.common.schematree.PathPatternTree; import org.apache.iotdb.db.mpp.plan.analyze.ExpressionAnalyzer; import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand; +import org.apache.iotdb.db.mpp.plan.expression.multi.FunctionExpression; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.ChildNodesSchemaScanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.metedata.read.ChildPathsSchemaScanNode; @@ -64,9 +67,6 @@ import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.GroupByTimeParameter; import org.apache.iotdb.db.mpp.plan.statement.component.OrderBy; import org.apache.iotdb.db.query.aggregation.AggregationType; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.leaf.TimeSeriesOperand; -import org.apache.iotdb.db.query.expression.multi.FunctionExpression; import org.apache.iotdb.db.utils.SchemaUtils; import org.apache.iotdb.tsfile.read.filter.basic.Filter; diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanner.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanner.java index 1265b4d6ab3b..4de3c11ab5f4 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanner.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanner.java @@ -21,6 +21,7 @@ import org.apache.iotdb.confignode.rpc.thrift.NodeManagementType; import org.apache.iotdb.db.mpp.common.MPPQueryContext; import org.apache.iotdb.db.mpp.plan.analyze.Analysis; +import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.db.mpp.plan.optimization.PlanOptimizer; import org.apache.iotdb.db.mpp.plan.planner.plan.LogicalQueryPlan; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNode; @@ -54,7 +55,6 @@ import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowChildPathsStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowDevicesStatement; import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowTimeSeriesStatement; -import org.apache.iotdb.db.query.expression.Expression; import java.util.ArrayList; import java.util.HashMap; diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/process/FilterNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/process/FilterNode.java index 338a519123bc..3b9a1ec4755f 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/process/FilterNode.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/process/FilterNode.java @@ -18,11 +18,11 @@ */ package org.apache.iotdb.db.mpp.plan.planner.plan.node.process; +import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeType; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanVisitor; -import org.apache.iotdb.db.query.expression.Expression; import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils; import java.nio.ByteBuffer; diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/process/FilterNullNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/process/FilterNullNode.java index c980aa5a9ef5..7c76a3cfc93a 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/process/FilterNullNode.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/process/FilterNullNode.java @@ -18,13 +18,13 @@ */ package org.apache.iotdb.db.mpp.plan.planner.plan.node.process; +import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeType; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanVisitor; import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.FilterNullParameter; import org.apache.iotdb.db.mpp.plan.statement.component.FilterNullPolicy; -import org.apache.iotdb.db.query.expression.Expression; import com.google.common.collect.ImmutableList; diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/process/TransformNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/process/TransformNode.java index 75dd96778045..7f9870fc18e7 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/process/TransformNode.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/process/TransformNode.java @@ -19,11 +19,11 @@ package org.apache.iotdb.db.mpp.plan.planner.plan.node.process; +import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeType; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanVisitor; -import org.apache.iotdb.db.query.expression.Expression; import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils; import com.google.common.collect.ImmutableList; diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/parameter/AggregationDescriptor.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/parameter/AggregationDescriptor.java index 74fd995005af..67ec816dfbc9 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/parameter/AggregationDescriptor.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/parameter/AggregationDescriptor.java @@ -19,8 +19,8 @@ package org.apache.iotdb.db.mpp.plan.planner.plan.parameter; +import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.db.query.aggregation.AggregationType; -import org.apache.iotdb.db.query.expression.Expression; import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils; import java.nio.ByteBuffer; diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/parameter/FilterNullParameter.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/parameter/FilterNullParameter.java index cf8b61f10ffc..c357fb296620 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/parameter/FilterNullParameter.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/parameter/FilterNullParameter.java @@ -19,8 +19,8 @@ package org.apache.iotdb.db.mpp.plan.planner.plan.parameter; +import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.db.mpp.plan.statement.component.FilterNullPolicy; -import org.apache.iotdb.db.query.expression.Expression; import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils; import java.nio.ByteBuffer; diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/FilterNullComponent.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/FilterNullComponent.java index 3c29d3149b92..e0e2c4c6e9b3 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/FilterNullComponent.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/FilterNullComponent.java @@ -19,8 +19,8 @@ package org.apache.iotdb.db.mpp.plan.statement.component; +import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.db.mpp.plan.statement.StatementNode; -import org.apache.iotdb.db.query.expression.Expression; import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils; import java.nio.ByteBuffer; diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/ResultColumn.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/ResultColumn.java index a138f6bfa14c..ae23d5e3d5be 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/ResultColumn.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/ResultColumn.java @@ -19,8 +19,8 @@ package org.apache.iotdb.db.mpp.plan.statement.component; +import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.db.mpp.plan.statement.StatementNode; -import org.apache.iotdb.db.query.expression.Expression; import java.util.Objects; diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/SelectComponent.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/SelectComponent.java index 3131d23ad5f1..cc92430f11d1 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/SelectComponent.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/SelectComponent.java @@ -19,8 +19,8 @@ package org.apache.iotdb.db.mpp.plan.statement.component; +import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.db.mpp.plan.statement.StatementNode; -import org.apache.iotdb.db.query.expression.Expression; import java.time.ZoneId; import java.util.ArrayList; diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/WhereCondition.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/WhereCondition.java index cd62ab874216..9014f345f710 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/WhereCondition.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/WhereCondition.java @@ -19,8 +19,8 @@ package org.apache.iotdb.db.mpp.plan.statement.component; +import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.db.mpp.plan.statement.StatementNode; -import org.apache.iotdb.db.query.expression.Expression; /** This class maintains information of {@code WHERE} clause. */ public class WhereCondition extends StatementNode { diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/crud/QueryStatement.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/crud/QueryStatement.java index a626c17e0c80..35a565f3e7f1 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/crud/QueryStatement.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/crud/QueryStatement.java @@ -23,6 +23,8 @@ import org.apache.iotdb.db.exception.sql.SemanticException; import org.apache.iotdb.db.mpp.plan.analyze.ExpressionAnalyzer; import org.apache.iotdb.db.mpp.plan.constant.StatementType; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand; import org.apache.iotdb.db.mpp.plan.statement.Statement; import org.apache.iotdb.db.mpp.plan.statement.StatementVisitor; import org.apache.iotdb.db.mpp.plan.statement.component.FillComponent; @@ -35,8 +37,6 @@ import org.apache.iotdb.db.mpp.plan.statement.component.ResultSetFormat; import org.apache.iotdb.db.mpp.plan.statement.component.SelectComponent; import org.apache.iotdb.db.mpp.plan.statement.component.WhereCondition; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.leaf.TimeSeriesOperand; import java.util.List; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/reader/LayerPointReader.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/api/LayerPointReader.java similarity index 96% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/reader/LayerPointReader.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/api/LayerPointReader.java index 3ee7c5a98505..9f831e7bece7 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/reader/LayerPointReader.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/api/LayerPointReader.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.reader; +package org.apache.iotdb.db.mpp.transformation.api; import org.apache.iotdb.db.exception.query.QueryProcessException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/reader/LayerRowReader.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/api/LayerRowReader.java similarity index 92% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/reader/LayerRowReader.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/api/LayerRowReader.java index e2a2f2387209..798a882a32c2 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/reader/LayerRowReader.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/api/LayerRowReader.java @@ -17,10 +17,10 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.reader; +package org.apache.iotdb.db.mpp.transformation.api; +import org.apache.iotdb.commons.udf.api.access.Row; import org.apache.iotdb.db.exception.query.QueryProcessException; -import org.apache.iotdb.db.query.udf.api.access.Row; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.io.IOException; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/reader/LayerRowWindowReader.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/api/LayerRowWindowReader.java similarity index 91% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/reader/LayerRowWindowReader.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/api/LayerRowWindowReader.java index 4e2b129fd313..699dda939807 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/reader/LayerRowWindowReader.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/api/LayerRowWindowReader.java @@ -17,10 +17,10 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.reader; +package org.apache.iotdb.db.mpp.transformation.api; +import org.apache.iotdb.commons.udf.api.access.RowWindow; import org.apache.iotdb.db.exception.query.QueryProcessException; -import org.apache.iotdb.db.query.udf.api.access.RowWindow; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.io.IOException; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/access/ElasticSerializableRowRecordListBackedMultiColumnRow.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/adapter/ElasticSerializableRowRecordListBackedMultiColumnRow.java similarity index 95% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/access/ElasticSerializableRowRecordListBackedMultiColumnRow.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/adapter/ElasticSerializableRowRecordListBackedMultiColumnRow.java index d29b0d5dc550..da49bc41fb74 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/access/ElasticSerializableRowRecordListBackedMultiColumnRow.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/adapter/ElasticSerializableRowRecordListBackedMultiColumnRow.java @@ -17,9 +17,9 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.access; +package org.apache.iotdb.db.mpp.transformation.dag.adapter; -import org.apache.iotdb.db.query.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.access.Row; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.utils.Binary; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/access/ElasticSerializableRowRecordListBackedMultiColumnWindow.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/adapter/ElasticSerializableRowRecordListBackedMultiColumnWindow.java similarity index 89% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/access/ElasticSerializableRowRecordListBackedMultiColumnWindow.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/adapter/ElasticSerializableRowRecordListBackedMultiColumnWindow.java index d38426751640..c7c6fbc3146a 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/access/ElasticSerializableRowRecordListBackedMultiColumnWindow.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/adapter/ElasticSerializableRowRecordListBackedMultiColumnWindow.java @@ -17,12 +17,12 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.access; +package org.apache.iotdb.db.mpp.transformation.dag.adapter; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.access.RowIterator; -import org.apache.iotdb.db.query.udf.api.access.RowWindow; -import org.apache.iotdb.db.query.udf.datastructure.row.ElasticSerializableRowRecordList; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.access.RowIterator; +import org.apache.iotdb.commons.udf.api.access.RowWindow; +import org.apache.iotdb.db.mpp.transformation.datastructure.row.ElasticSerializableRowRecordList; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.io.IOException; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/access/ElasticSerializableRowRecordListBackedMultiColumnWindowIterator.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/adapter/ElasticSerializableRowRecordListBackedMultiColumnWindowIterator.java similarity index 87% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/access/ElasticSerializableRowRecordListBackedMultiColumnWindowIterator.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/adapter/ElasticSerializableRowRecordListBackedMultiColumnWindowIterator.java index 801e43f35d06..7333fd58f302 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/access/ElasticSerializableRowRecordListBackedMultiColumnWindowIterator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/adapter/ElasticSerializableRowRecordListBackedMultiColumnWindowIterator.java @@ -17,11 +17,11 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.access; +package org.apache.iotdb.db.mpp.transformation.dag.adapter; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.access.RowIterator; -import org.apache.iotdb.db.query.udf.datastructure.row.ElasticSerializableRowRecordList; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.access.RowIterator; +import org.apache.iotdb.db.mpp.transformation.datastructure.row.ElasticSerializableRowRecordList; import java.io.IOException; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/access/ElasticSerializableTVListBackedSingleColumnRow.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/adapter/ElasticSerializableTVListBackedSingleColumnRow.java similarity index 92% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/access/ElasticSerializableTVListBackedSingleColumnRow.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/adapter/ElasticSerializableTVListBackedSingleColumnRow.java index 1207f32ee191..609f8faadc1c 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/access/ElasticSerializableTVListBackedSingleColumnRow.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/adapter/ElasticSerializableTVListBackedSingleColumnRow.java @@ -17,10 +17,10 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.access; +package org.apache.iotdb.db.mpp.transformation.dag.adapter; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.datastructure.tv.ElasticSerializableTVList; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.db.mpp.transformation.datastructure.tv.ElasticSerializableTVList; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.utils.Binary; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/access/ElasticSerializableTVListBackedSingleColumnWindow.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/adapter/ElasticSerializableTVListBackedSingleColumnWindow.java similarity index 88% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/access/ElasticSerializableTVListBackedSingleColumnWindow.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/adapter/ElasticSerializableTVListBackedSingleColumnWindow.java index f8fcf92d2208..709f661ae50d 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/access/ElasticSerializableTVListBackedSingleColumnWindow.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/adapter/ElasticSerializableTVListBackedSingleColumnWindow.java @@ -17,12 +17,12 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.access; +package org.apache.iotdb.db.mpp.transformation.dag.adapter; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.access.RowIterator; -import org.apache.iotdb.db.query.udf.api.access.RowWindow; -import org.apache.iotdb.db.query.udf.datastructure.tv.ElasticSerializableTVList; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.access.RowIterator; +import org.apache.iotdb.commons.udf.api.access.RowWindow; +import org.apache.iotdb.db.mpp.transformation.datastructure.tv.ElasticSerializableTVList; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; public class ElasticSerializableTVListBackedSingleColumnWindow implements RowWindow { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/access/ElasticSerializableTVListBackedSingleColumnWindowIterator.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/adapter/ElasticSerializableTVListBackedSingleColumnWindowIterator.java similarity index 86% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/access/ElasticSerializableTVListBackedSingleColumnWindowIterator.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/adapter/ElasticSerializableTVListBackedSingleColumnWindowIterator.java index 7dc35d6c3f9f..beaefb13bb55 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/access/ElasticSerializableTVListBackedSingleColumnWindowIterator.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/adapter/ElasticSerializableTVListBackedSingleColumnWindowIterator.java @@ -17,11 +17,11 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.access; +package org.apache.iotdb.db.mpp.transformation.dag.adapter; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.access.RowIterator; -import org.apache.iotdb.db.query.udf.datastructure.tv.ElasticSerializableTVList; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.access.RowIterator; +import org.apache.iotdb.db.mpp.transformation.datastructure.tv.ElasticSerializableTVList; import java.io.IOException; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/access/LayerPointReaderBackedSingleColumnRow.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/adapter/LayerPointReaderBackedSingleColumnRow.java similarity index 93% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/access/LayerPointReaderBackedSingleColumnRow.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/adapter/LayerPointReaderBackedSingleColumnRow.java index a7258788bf9c..48a0badabf55 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/access/LayerPointReaderBackedSingleColumnRow.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/adapter/LayerPointReaderBackedSingleColumnRow.java @@ -17,10 +17,10 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.access; +package org.apache.iotdb.db.mpp.transformation.dag.adapter; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.utils.Binary; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/DAGBuilder.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/builder/DAGBuilder.java similarity index 85% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/DAGBuilder.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/builder/DAGBuilder.java index 270482b1e6ee..abe194bf2ae3 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/DAGBuilder.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/builder/DAGBuilder.java @@ -17,13 +17,16 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.layer; +package org.apache.iotdb.db.mpp.transformation.dag.builder; import org.apache.iotdb.db.exception.query.QueryProcessException; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ResultColumn; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.dag.input.QueryDataSetInputLayer; +import org.apache.iotdb.db.mpp.transformation.dag.intermediate.IntermediateLayer; +import org.apache.iotdb.db.mpp.transformation.dag.memory.LayerMemoryAssigner; import org.apache.iotdb.db.qp.physical.crud.UDTFPlan; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.ResultColumn; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.io.IOException; @@ -34,7 +37,7 @@ public class DAGBuilder { private final long queryId; private final UDTFPlan udtfPlan; - private final RawQueryInputLayer rawTimeSeriesInputLayer; + private final QueryDataSetInputLayer rawTimeSeriesInputLayer; // input private final Expression[] resultColumnExpressions; @@ -51,7 +54,7 @@ public class DAGBuilder { private final Map expressionDataTypeMap; public DAGBuilder( - long queryId, UDTFPlan udtfPlan, RawQueryInputLayer inputLayer, float memoryBudgetInMB) { + long queryId, UDTFPlan udtfPlan, QueryDataSetInputLayer inputLayer, float memoryBudgetInMB) { this.queryId = queryId; this.udtfPlan = udtfPlan; this.rawTimeSeriesInputLayer = inputLayer; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/EvaluationDAGBuilder.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/builder/EvaluationDAGBuilder.java similarity index 86% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/EvaluationDAGBuilder.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/builder/EvaluationDAGBuilder.java index d083191b34da..7a7a76bf5ac6 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/EvaluationDAGBuilder.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/builder/EvaluationDAGBuilder.java @@ -17,14 +17,17 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.layer; +package org.apache.iotdb.db.mpp.transformation.dag.builder; import org.apache.iotdb.db.exception.query.QueryProcessException; import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; +import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.InputLocation; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.udf.core.executor.UDTFContext; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.dag.input.QueryDataSetInputLayer; +import org.apache.iotdb.db.mpp.transformation.dag.intermediate.IntermediateLayer; +import org.apache.iotdb.db.mpp.transformation.dag.memory.LayerMemoryAssigner; +import org.apache.iotdb.db.mpp.transformation.dag.udf.UDTFContext; import java.io.IOException; import java.util.HashMap; @@ -35,7 +38,7 @@ public class EvaluationDAGBuilder { private final long queryId; - private final RawQueryInputLayer inputLayer; + private final QueryDataSetInputLayer inputLayer; private final Map> inputLocations; private final Expression[] outputExpressions; @@ -55,7 +58,7 @@ public class EvaluationDAGBuilder { public EvaluationDAGBuilder( long queryId, - RawQueryInputLayer inputLayer, + QueryDataSetInputLayer inputLayer, Map> inputLocations, Expression[] outputExpressions, TypeProvider typeProvider, diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/reader/ConstantLayerPointReader.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/input/ConstantInputReader.java similarity index 90% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/reader/ConstantLayerPointReader.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/input/ConstantInputReader.java index c0d99e918946..a8cf12f97230 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/reader/ConstantLayerPointReader.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/input/ConstantInputReader.java @@ -17,10 +17,11 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.reader; +package org.apache.iotdb.db.mpp.transformation.dag.input; import org.apache.iotdb.db.exception.query.QueryProcessException; -import org.apache.iotdb.db.query.expression.leaf.ConstantOperand; +import org.apache.iotdb.db.mpp.plan.expression.leaf.ConstantOperand; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; import org.apache.iotdb.db.utils.CommonUtils; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.utils.Binary; @@ -30,7 +31,7 @@ import java.io.IOException; /** LayerPointReader for constants. */ -public class ConstantLayerPointReader implements LayerPointReader { +public class ConstantInputReader implements LayerPointReader { private final ConstantOperand expression; @@ -41,7 +42,7 @@ public class ConstantLayerPointReader implements LayerPointReader { protected boolean cachedBoolean; protected Binary cachedBinary; - public ConstantLayerPointReader(ConstantOperand expression) throws QueryProcessException { + public ConstantInputReader(ConstantOperand expression) throws QueryProcessException { this.expression = Validate.notNull(expression); Object value = diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/RawQueryInputLayer.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/input/QueryDataSetInputLayer.java similarity index 93% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/RawQueryInputLayer.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/input/QueryDataSetInputLayer.java index 4f5288d77147..9490461bb00a 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/RawQueryInputLayer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/input/QueryDataSetInputLayer.java @@ -17,19 +17,20 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.layer; +package org.apache.iotdb.db.mpp.transformation.dag.input; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.exception.query.QueryProcessException; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.dag.memory.SafetyLine; +import org.apache.iotdb.db.mpp.transformation.dag.memory.SafetyLine.SafetyPile; +import org.apache.iotdb.db.mpp.transformation.datastructure.row.ElasticSerializableRowRecordList; import org.apache.iotdb.db.qp.physical.crud.UDTFPlan; import org.apache.iotdb.db.query.dataset.IUDFInputDataSet; import org.apache.iotdb.db.query.dataset.RawQueryDataSetWithValueFilter; import org.apache.iotdb.db.query.dataset.UDFRawQueryInputDataSetWithoutValueFilter; import org.apache.iotdb.db.query.reader.series.IReaderByTimestamp; import org.apache.iotdb.db.query.reader.series.ManagedSeriesReader; -import org.apache.iotdb.db.query.udf.core.layer.SafetyLine.SafetyPile; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; -import org.apache.iotdb.db.query.udf.datastructure.row.ElasticSerializableRowRecordList; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.read.query.timegenerator.TimeGenerator; import org.apache.iotdb.tsfile.utils.Binary; @@ -37,7 +38,7 @@ import java.io.IOException; import java.util.List; -public class RawQueryInputLayer { +public class QueryDataSetInputLayer { private IUDFInputDataSet queryDataSet; private TSDataType[] dataTypes; @@ -47,7 +48,7 @@ public class RawQueryInputLayer { private SafetyLine safetyLine; /** InputLayerWithoutValueFilter */ - public RawQueryInputLayer( + public QueryDataSetInputLayer( long queryId, float memoryBudgetInMB, UDTFPlan queryPlan, List readers) throws QueryProcessException, IOException, InterruptedException { construct( @@ -57,7 +58,7 @@ public RawQueryInputLayer( } /** InputLayerWithValueFilter */ - public RawQueryInputLayer( + public QueryDataSetInputLayer( long queryId, float memoryBudgetInMB, List paths, @@ -74,7 +75,7 @@ public RawQueryInputLayer( paths, dataTypes, timeGenerator, readers, readerToIndexList, cached, true)); } - public RawQueryInputLayer(long queryId, float memoryBudgetInMB, IUDFInputDataSet queryDataSet) + public QueryDataSetInputLayer(long queryId, float memoryBudgetInMB, IUDFInputDataSet queryDataSet) throws QueryProcessException { construct(queryId, memoryBudgetInMB, queryDataSet); } @@ -94,10 +95,6 @@ public void updateRowRecordListEvictionUpperBound() { rowRecordList.setEvictionUpperBound(safetyLine.getSafetyLine()); } - public int getInputColumnCount() { - return dataTypes.length; - } - public LayerPointReader constructTimePointReader() { return new TimePointReader(); } diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/TsBlockInputDataSet.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/input/TsBlockInputDataSet.java similarity index 97% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/TsBlockInputDataSet.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/input/TsBlockInputDataSet.java index ff9130d42769..135c9c1f8d21 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/TsBlockInputDataSet.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/input/TsBlockInputDataSet.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.layer; +package org.apache.iotdb.db.mpp.transformation.dag.input; import org.apache.iotdb.db.mpp.execution.operator.Operator; import org.apache.iotdb.db.query.dataset.IUDFInputDataSet; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/ConstantIntermediateLayer.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/intermediate/ConstantIntermediateLayer.java similarity index 75% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/ConstantIntermediateLayer.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/intermediate/ConstantIntermediateLayer.java index b82b268976e3..1af198720a4f 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/ConstantIntermediateLayer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/intermediate/ConstantIntermediateLayer.java @@ -17,16 +17,16 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.layer; +package org.apache.iotdb.db.mpp.transformation.dag.intermediate; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingTimeWindowAccessStrategy; import org.apache.iotdb.db.exception.query.QueryProcessException; -import org.apache.iotdb.db.query.expression.leaf.ConstantOperand; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingTimeWindowAccessStrategy; -import org.apache.iotdb.db.query.udf.core.reader.ConstantLayerPointReader; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; -import org.apache.iotdb.db.query.udf.core.reader.LayerRowReader; -import org.apache.iotdb.db.query.udf.core.reader.LayerRowWindowReader; +import org.apache.iotdb.db.mpp.plan.expression.leaf.ConstantOperand; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.api.LayerRowReader; +import org.apache.iotdb.db.mpp.transformation.api.LayerRowWindowReader; +import org.apache.iotdb.db.mpp.transformation.dag.input.ConstantInputReader; /** IntermediateLayer for constants. */ public class ConstantIntermediateLayer extends IntermediateLayer { @@ -36,7 +36,7 @@ public class ConstantIntermediateLayer extends IntermediateLayer { public ConstantIntermediateLayer(ConstantOperand expression, long queryId, float memoryBudgetInMB) throws QueryProcessException { super(expression, queryId, memoryBudgetInMB); - constantLayerPointReaderCache = new ConstantLayerPointReader(expression); + constantLayerPointReaderCache = new ConstantInputReader(expression); } @Override diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/IntermediateLayer.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/intermediate/IntermediateLayer.java similarity index 81% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/IntermediateLayer.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/intermediate/IntermediateLayer.java index 890a6b4259e7..18fc3f84d988 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/IntermediateLayer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/intermediate/IntermediateLayer.java @@ -17,16 +17,16 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.layer; +package org.apache.iotdb.db.mpp.transformation.dag.intermediate; +import org.apache.iotdb.commons.udf.api.customizer.strategy.AccessStrategy; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingTimeWindowAccessStrategy; import org.apache.iotdb.db.exception.query.QueryProcessException; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.AccessStrategy; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingTimeWindowAccessStrategy; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; -import org.apache.iotdb.db.query.udf.core.reader.LayerRowReader; -import org.apache.iotdb.db.query.udf.core.reader.LayerRowWindowReader; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.api.LayerRowReader; +import org.apache.iotdb.db.mpp.transformation.api.LayerRowWindowReader; import java.io.IOException; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/MultiInputColumnIntermediateLayer.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/intermediate/MultiInputColumnIntermediateLayer.java similarity index 91% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/MultiInputColumnIntermediateLayer.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/intermediate/MultiInputColumnIntermediateLayer.java index 843bd85504e8..f7d14a396a8b 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/MultiInputColumnIntermediateLayer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/intermediate/MultiInputColumnIntermediateLayer.java @@ -17,21 +17,23 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.layer; +package org.apache.iotdb.db.mpp.transformation.dag.intermediate; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.access.RowWindow; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingTimeWindowAccessStrategy; import org.apache.iotdb.db.exception.query.QueryProcessException; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.api.LayerRowReader; +import org.apache.iotdb.db.mpp.transformation.api.LayerRowWindowReader; +import org.apache.iotdb.db.mpp.transformation.dag.adapter.ElasticSerializableRowRecordListBackedMultiColumnRow; +import org.apache.iotdb.db.mpp.transformation.dag.adapter.ElasticSerializableRowRecordListBackedMultiColumnWindow; +import org.apache.iotdb.db.mpp.transformation.dag.util.InputRowUtils; +import org.apache.iotdb.db.mpp.transformation.dag.util.LayerCacheUtils; +import org.apache.iotdb.db.mpp.transformation.datastructure.row.ElasticSerializableRowRecordList; import org.apache.iotdb.db.query.dataset.IUDFInputDataSet; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.access.RowWindow; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingTimeWindowAccessStrategy; -import org.apache.iotdb.db.query.udf.core.access.ElasticSerializableRowRecordListBackedMultiColumnRow; -import org.apache.iotdb.db.query.udf.core.access.ElasticSerializableRowRecordListBackedMultiColumnWindow; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; -import org.apache.iotdb.db.query.udf.core.reader.LayerRowReader; -import org.apache.iotdb.db.query.udf.core.reader.LayerRowWindowReader; -import org.apache.iotdb.db.query.udf.datastructure.row.ElasticSerializableRowRecordList; import org.apache.iotdb.db.utils.datastructure.TimeSelector; import org.apache.iotdb.tsfile.exception.write.UnSupportedDataTypeException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/SingleInputColumnMultiReferenceIntermediateLayer.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/intermediate/SingleInputColumnMultiReferenceIntermediateLayer.java similarity index 90% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/SingleInputColumnMultiReferenceIntermediateLayer.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/intermediate/SingleInputColumnMultiReferenceIntermediateLayer.java index ba4f91a66f05..7be2b1fdfa39 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/SingleInputColumnMultiReferenceIntermediateLayer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/intermediate/SingleInputColumnMultiReferenceIntermediateLayer.java @@ -17,21 +17,23 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.layer; +package org.apache.iotdb.db.mpp.transformation.dag.intermediate; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.access.RowWindow; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingTimeWindowAccessStrategy; import org.apache.iotdb.db.exception.query.QueryProcessException; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.access.RowWindow; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingTimeWindowAccessStrategy; -import org.apache.iotdb.db.query.udf.core.access.ElasticSerializableTVListBackedSingleColumnRow; -import org.apache.iotdb.db.query.udf.core.access.ElasticSerializableTVListBackedSingleColumnWindow; -import org.apache.iotdb.db.query.udf.core.layer.SafetyLine.SafetyPile; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; -import org.apache.iotdb.db.query.udf.core.reader.LayerRowReader; -import org.apache.iotdb.db.query.udf.core.reader.LayerRowWindowReader; -import org.apache.iotdb.db.query.udf.datastructure.tv.ElasticSerializableTVList; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.api.LayerRowReader; +import org.apache.iotdb.db.mpp.transformation.api.LayerRowWindowReader; +import org.apache.iotdb.db.mpp.transformation.dag.adapter.ElasticSerializableTVListBackedSingleColumnRow; +import org.apache.iotdb.db.mpp.transformation.dag.adapter.ElasticSerializableTVListBackedSingleColumnWindow; +import org.apache.iotdb.db.mpp.transformation.dag.memory.SafetyLine; +import org.apache.iotdb.db.mpp.transformation.dag.memory.SafetyLine.SafetyPile; +import org.apache.iotdb.db.mpp.transformation.dag.util.LayerCacheUtils; +import org.apache.iotdb.db.mpp.transformation.datastructure.tv.ElasticSerializableTVList; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.utils.Binary; @@ -55,8 +57,7 @@ public SingleInputColumnMultiReferenceIntermediateLayer( Expression expression, long queryId, float memoryBudgetInMB, - LayerPointReader parentLayerPointReader) - throws QueryProcessException { + LayerPointReader parentLayerPointReader) { super(expression, queryId, memoryBudgetInMB); this.parentLayerPointReader = parentLayerPointReader; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/SingleInputColumnSingleReferenceIntermediateLayer.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/intermediate/SingleInputColumnSingleReferenceIntermediateLayer.java similarity index 89% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/SingleInputColumnSingleReferenceIntermediateLayer.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/intermediate/SingleInputColumnSingleReferenceIntermediateLayer.java index 29549ccdd015..846b77b73846 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/SingleInputColumnSingleReferenceIntermediateLayer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/intermediate/SingleInputColumnSingleReferenceIntermediateLayer.java @@ -17,20 +17,21 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.layer; +package org.apache.iotdb.db.mpp.transformation.dag.intermediate; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.access.RowWindow; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; +import org.apache.iotdb.commons.udf.api.customizer.strategy.SlidingTimeWindowAccessStrategy; import org.apache.iotdb.db.exception.query.QueryProcessException; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.access.RowWindow; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingSizeWindowAccessStrategy; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.SlidingTimeWindowAccessStrategy; -import org.apache.iotdb.db.query.udf.core.access.ElasticSerializableTVListBackedSingleColumnWindow; -import org.apache.iotdb.db.query.udf.core.access.LayerPointReaderBackedSingleColumnRow; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; -import org.apache.iotdb.db.query.udf.core.reader.LayerRowReader; -import org.apache.iotdb.db.query.udf.core.reader.LayerRowWindowReader; -import org.apache.iotdb.db.query.udf.datastructure.tv.ElasticSerializableTVList; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.api.LayerRowReader; +import org.apache.iotdb.db.mpp.transformation.api.LayerRowWindowReader; +import org.apache.iotdb.db.mpp.transformation.dag.adapter.ElasticSerializableTVListBackedSingleColumnWindow; +import org.apache.iotdb.db.mpp.transformation.dag.adapter.LayerPointReaderBackedSingleColumnRow; +import org.apache.iotdb.db.mpp.transformation.dag.util.LayerCacheUtils; +import org.apache.iotdb.db.mpp.transformation.datastructure.tv.ElasticSerializableTVList; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.slf4j.Logger; @@ -113,8 +114,7 @@ public boolean isCurrentNull() { @Override protected LayerRowWindowReader constructRowSlidingSizeWindowReader( - SlidingSizeWindowAccessStrategy strategy, float memoryBudgetInMB) - throws QueryProcessException { + SlidingSizeWindowAccessStrategy strategy, float memoryBudgetInMB) { return new LayerRowWindowReader() { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/LayerMemoryAssigner.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/memory/LayerMemoryAssigner.java similarity index 91% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/LayerMemoryAssigner.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/memory/LayerMemoryAssigner.java index 03f6fbe8b241..928c6628837d 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/LayerMemoryAssigner.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/memory/LayerMemoryAssigner.java @@ -17,10 +17,10 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.layer; +package org.apache.iotdb.db.mpp.transformation.dag.memory; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.multi.FunctionExpression; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.multi.FunctionExpression; import java.util.HashMap; import java.util.Map; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/SafetyLine.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/memory/SafetyLine.java similarity index 94% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/SafetyLine.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/memory/SafetyLine.java index 9f36b0540ca6..cdd61ab82469 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/SafetyLine.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/memory/SafetyLine.java @@ -17,9 +17,9 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.layer; +package org.apache.iotdb.db.mpp.transformation.dag.memory; -import org.apache.iotdb.db.query.udf.datastructure.tv.ElasticSerializableTVList; +import org.apache.iotdb.db.mpp.transformation.datastructure.tv.ElasticSerializableTVList; /** Tells the {@link ElasticSerializableTVList} if it is safe to remove a cache block. */ public class SafetyLine { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/Transformer.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/Transformer.java similarity index 94% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/Transformer.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/Transformer.java index a666924a5c4f..17d91d60a201 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/Transformer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/Transformer.java @@ -17,10 +17,10 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.transformer; +package org.apache.iotdb.db.mpp.transformation.dag.transformer; import org.apache.iotdb.db.exception.query.QueryProcessException; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; import org.apache.iotdb.tsfile.utils.Binary; import java.io.IOException; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/ArithmeticAdditionTransformer.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/ArithmeticAdditionTransformer.java similarity index 89% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/ArithmeticAdditionTransformer.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/ArithmeticAdditionTransformer.java index 439378f39bf1..1940fcbf2e49 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/ArithmeticAdditionTransformer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/ArithmeticAdditionTransformer.java @@ -17,9 +17,9 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.transformer.binary; +package org.apache.iotdb.db.mpp.transformation.dag.transformer.binary; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; public class ArithmeticAdditionTransformer extends ArithmeticBinaryTransformer { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/ArithmeticBinaryTransformer.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/ArithmeticBinaryTransformer.java similarity index 94% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/ArithmeticBinaryTransformer.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/ArithmeticBinaryTransformer.java index 751fc534d62f..5e3da9f1d6cd 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/ArithmeticBinaryTransformer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/ArithmeticBinaryTransformer.java @@ -17,10 +17,10 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.transformer.binary; +package org.apache.iotdb.db.mpp.transformation.dag.transformer.binary; import org.apache.iotdb.db.exception.query.QueryProcessException; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; import org.apache.iotdb.tsfile.exception.write.UnSupportedDataTypeException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/ArithmeticDivisionTransformer.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/ArithmeticDivisionTransformer.java similarity index 89% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/ArithmeticDivisionTransformer.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/ArithmeticDivisionTransformer.java index 30fb3d248fcf..8792fc439320 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/ArithmeticDivisionTransformer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/ArithmeticDivisionTransformer.java @@ -17,9 +17,9 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.transformer.binary; +package org.apache.iotdb.db.mpp.transformation.dag.transformer.binary; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; public class ArithmeticDivisionTransformer extends ArithmeticBinaryTransformer { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/ArithmeticModuloTransformer.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/ArithmeticModuloTransformer.java similarity index 89% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/ArithmeticModuloTransformer.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/ArithmeticModuloTransformer.java index a22f2d44008e..1b56cdfaabd1 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/ArithmeticModuloTransformer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/ArithmeticModuloTransformer.java @@ -17,9 +17,9 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.transformer.binary; +package org.apache.iotdb.db.mpp.transformation.dag.transformer.binary; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; public class ArithmeticModuloTransformer extends ArithmeticBinaryTransformer { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/ArithmeticMultiplicationTransformer.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/ArithmeticMultiplicationTransformer.java similarity index 89% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/ArithmeticMultiplicationTransformer.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/ArithmeticMultiplicationTransformer.java index b24522267be1..1a1733c2451d 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/ArithmeticMultiplicationTransformer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/ArithmeticMultiplicationTransformer.java @@ -17,9 +17,9 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.transformer.binary; +package org.apache.iotdb.db.mpp.transformation.dag.transformer.binary; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; public class ArithmeticMultiplicationTransformer extends ArithmeticBinaryTransformer { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/ArithmeticSubtractionTransformer.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/ArithmeticSubtractionTransformer.java similarity index 89% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/ArithmeticSubtractionTransformer.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/ArithmeticSubtractionTransformer.java index 6659a40ff05b..dde1b40ec848 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/ArithmeticSubtractionTransformer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/ArithmeticSubtractionTransformer.java @@ -17,9 +17,9 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.transformer.binary; +package org.apache.iotdb.db.mpp.transformation.dag.transformer.binary; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; public class ArithmeticSubtractionTransformer extends ArithmeticBinaryTransformer { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/BinaryTransformer.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/BinaryTransformer.java similarity index 95% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/BinaryTransformer.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/BinaryTransformer.java index c99a4e1b3c1e..13cd2a4d5bb9 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/BinaryTransformer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/BinaryTransformer.java @@ -17,11 +17,11 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.transformer.binary; +package org.apache.iotdb.db.mpp.transformation.dag.transformer.binary; import org.apache.iotdb.db.exception.query.QueryProcessException; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; -import org.apache.iotdb.db.query.udf.core.transformer.Transformer; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.Transformer; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.io.IOException; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareBinaryTransformer.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/CompareBinaryTransformer.java similarity index 95% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareBinaryTransformer.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/CompareBinaryTransformer.java index 42b42e105837..e81b4e96c4f6 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareBinaryTransformer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/CompareBinaryTransformer.java @@ -17,10 +17,10 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.transformer.binary; +package org.apache.iotdb.db.mpp.transformation.dag.transformer.binary; import org.apache.iotdb.db.exception.query.QueryProcessException; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; import org.apache.iotdb.tsfile.exception.write.UnSupportedDataTypeException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareEqualToTransformer.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/CompareEqualToTransformer.java similarity index 92% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareEqualToTransformer.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/CompareEqualToTransformer.java index af6b2c8d651b..4ea813e0834f 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareEqualToTransformer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/CompareEqualToTransformer.java @@ -17,9 +17,9 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.transformer.binary; +package org.apache.iotdb.db.mpp.transformation.dag.transformer.binary; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; public class CompareEqualToTransformer extends CompareBinaryTransformer { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareGreaterEqualTransformer.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/CompareGreaterEqualTransformer.java similarity index 92% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareGreaterEqualTransformer.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/CompareGreaterEqualTransformer.java index 1e98bfe423cb..b5d53f0dc9b6 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareGreaterEqualTransformer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/CompareGreaterEqualTransformer.java @@ -17,9 +17,9 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.transformer.binary; +package org.apache.iotdb.db.mpp.transformation.dag.transformer.binary; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; public class CompareGreaterEqualTransformer extends CompareBinaryTransformer { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareGreaterThanTransformer.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/CompareGreaterThanTransformer.java similarity index 92% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareGreaterThanTransformer.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/CompareGreaterThanTransformer.java index 9e7a5ea3baba..798df4a1b755 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareGreaterThanTransformer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/CompareGreaterThanTransformer.java @@ -17,9 +17,9 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.transformer.binary; +package org.apache.iotdb.db.mpp.transformation.dag.transformer.binary; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; public class CompareGreaterThanTransformer extends CompareBinaryTransformer { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareLessEqualTransformer.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/CompareLessEqualTransformer.java similarity index 92% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareLessEqualTransformer.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/CompareLessEqualTransformer.java index 0c53e869493a..9b322c729757 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareLessEqualTransformer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/CompareLessEqualTransformer.java @@ -17,9 +17,9 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.transformer.binary; +package org.apache.iotdb.db.mpp.transformation.dag.transformer.binary; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; public class CompareLessEqualTransformer extends CompareBinaryTransformer { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareLessThanTransformer.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/CompareLessThanTransformer.java similarity index 92% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareLessThanTransformer.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/CompareLessThanTransformer.java index 9e77e2aa215c..19a65ce8efec 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareLessThanTransformer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/CompareLessThanTransformer.java @@ -17,9 +17,9 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.transformer.binary; +package org.apache.iotdb.db.mpp.transformation.dag.transformer.binary; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; public class CompareLessThanTransformer extends CompareBinaryTransformer { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareNonEqualTransformer.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/CompareNonEqualTransformer.java similarity index 92% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareNonEqualTransformer.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/CompareNonEqualTransformer.java index 024c54deb5bb..65bed1face35 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/CompareNonEqualTransformer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/CompareNonEqualTransformer.java @@ -17,9 +17,9 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.transformer.binary; +package org.apache.iotdb.db.mpp.transformation.dag.transformer.binary; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; import org.apache.iotdb.tsfile.exception.write.UnSupportedDataTypeException; public class CompareNonEqualTransformer extends CompareBinaryTransformer { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/LogicAndTransformer.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/LogicAndTransformer.java similarity index 88% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/LogicAndTransformer.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/LogicAndTransformer.java index 3dc5c1d24087..f2c25b46b825 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/LogicAndTransformer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/LogicAndTransformer.java @@ -17,9 +17,9 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.transformer.binary; +package org.apache.iotdb.db.mpp.transformation.dag.transformer.binary; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; public class LogicAndTransformer extends LogicBinaryTransformer { @@ -27,6 +27,7 @@ public LogicAndTransformer(LayerPointReader leftPointReader, LayerPointReader ri super(leftPointReader, rightPointReader); } + @Override protected boolean evaluate(boolean leftOperand, boolean rightOperand) { return leftOperand && rightOperand; } diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/LogicBinaryTransformer.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/LogicBinaryTransformer.java similarity index 93% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/LogicBinaryTransformer.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/LogicBinaryTransformer.java index 7eee752775da..15cf8aa75cdd 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/LogicBinaryTransformer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/LogicBinaryTransformer.java @@ -17,10 +17,10 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.transformer.binary; +package org.apache.iotdb.db.mpp.transformation.dag.transformer.binary; import org.apache.iotdb.db.exception.query.QueryProcessException; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; import org.apache.iotdb.tsfile.exception.write.UnSupportedDataTypeException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/LogicOrTransformer.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/LogicOrTransformer.java similarity index 89% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/LogicOrTransformer.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/LogicOrTransformer.java index b8ac911a918d..2ef1e356149f 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/binary/LogicOrTransformer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/binary/LogicOrTransformer.java @@ -17,9 +17,9 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.transformer.binary; +package org.apache.iotdb.db.mpp.transformation.dag.transformer.binary; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; public class LogicOrTransformer extends LogicBinaryTransformer { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/multi/UDFQueryRowTransformer.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/multi/UDFQueryRowTransformer.java similarity index 88% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/multi/UDFQueryRowTransformer.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/multi/UDFQueryRowTransformer.java index 354a10273684..c96af9fd7fc1 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/multi/UDFQueryRowTransformer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/multi/UDFQueryRowTransformer.java @@ -17,11 +17,11 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.transformer.multi; +package org.apache.iotdb.db.mpp.transformation.dag.transformer.multi; import org.apache.iotdb.db.exception.query.QueryProcessException; -import org.apache.iotdb.db.query.udf.core.executor.UDTFExecutor; -import org.apache.iotdb.db.query.udf.core.reader.LayerRowReader; +import org.apache.iotdb.db.mpp.transformation.api.LayerRowReader; +import org.apache.iotdb.db.mpp.transformation.dag.udf.UDTFExecutor; import java.io.IOException; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/multi/UDFQueryRowWindowTransformer.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/multi/UDFQueryRowWindowTransformer.java similarity index 87% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/multi/UDFQueryRowWindowTransformer.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/multi/UDFQueryRowWindowTransformer.java index 1f810694e2ec..65188bed3d25 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/multi/UDFQueryRowWindowTransformer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/multi/UDFQueryRowWindowTransformer.java @@ -17,11 +17,11 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.transformer.multi; +package org.apache.iotdb.db.mpp.transformation.dag.transformer.multi; import org.apache.iotdb.db.exception.query.QueryProcessException; -import org.apache.iotdb.db.query.udf.core.executor.UDTFExecutor; -import org.apache.iotdb.db.query.udf.core.reader.LayerRowWindowReader; +import org.apache.iotdb.db.mpp.transformation.api.LayerRowWindowReader; +import org.apache.iotdb.db.mpp.transformation.dag.udf.UDTFExecutor; import java.io.IOException; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/multi/UDFQueryTransformer.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/multi/UDFQueryTransformer.java similarity index 92% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/multi/UDFQueryTransformer.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/multi/UDFQueryTransformer.java index 28b18f6cfc23..77e5a2ffa7da 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/multi/UDFQueryTransformer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/multi/UDFQueryTransformer.java @@ -17,12 +17,12 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.transformer.multi; +package org.apache.iotdb.db.mpp.transformation.dag.transformer.multi; import org.apache.iotdb.db.exception.query.QueryProcessException; -import org.apache.iotdb.db.query.udf.core.executor.UDTFExecutor; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; -import org.apache.iotdb.db.query.udf.core.transformer.Transformer; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.Transformer; +import org.apache.iotdb.db.mpp.transformation.dag.udf.UDTFExecutor; import org.apache.iotdb.tsfile.exception.write.UnSupportedDataTypeException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/unary/ArithmeticNegationTransformer.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/unary/ArithmeticNegationTransformer.java similarity index 93% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/unary/ArithmeticNegationTransformer.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/unary/ArithmeticNegationTransformer.java index 1dbd1ba04682..06bad592de3c 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/unary/ArithmeticNegationTransformer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/unary/ArithmeticNegationTransformer.java @@ -17,10 +17,10 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.transformer.unary; +package org.apache.iotdb.db.mpp.transformation.dag.transformer.unary; import org.apache.iotdb.db.exception.query.QueryProcessException; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.io.IOException; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/unary/InTransformer.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/unary/InTransformer.java similarity index 97% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/unary/InTransformer.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/unary/InTransformer.java index 5d219f50cfc9..05673d31191d 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/unary/InTransformer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/unary/InTransformer.java @@ -17,10 +17,10 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.transformer.unary; +package org.apache.iotdb.db.mpp.transformation.dag.transformer.unary; import org.apache.iotdb.db.exception.query.QueryProcessException; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.utils.Binary; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/unary/LogicNotTransformer.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/unary/LogicNotTransformer.java similarity index 92% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/unary/LogicNotTransformer.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/unary/LogicNotTransformer.java index 558bd6caea2c..da23d377f441 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/unary/LogicNotTransformer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/unary/LogicNotTransformer.java @@ -17,10 +17,10 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.transformer.unary; +package org.apache.iotdb.db.mpp.transformation.dag.transformer.unary; import org.apache.iotdb.db.exception.query.QueryProcessException; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; import org.apache.iotdb.tsfile.exception.write.UnSupportedDataTypeException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/unary/RegularTransformer.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/unary/RegularTransformer.java similarity index 93% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/unary/RegularTransformer.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/unary/RegularTransformer.java index e7095eb0105c..862c7fe563f1 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/unary/RegularTransformer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/unary/RegularTransformer.java @@ -17,10 +17,10 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.transformer.unary; +package org.apache.iotdb.db.mpp.transformation.dag.transformer.unary; import org.apache.iotdb.db.exception.query.QueryProcessException; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; import org.apache.iotdb.tsfile.exception.write.UnSupportedDataTypeException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.utils.Binary; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/unary/TransparentTransformer.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/unary/TransparentTransformer.java similarity index 94% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/unary/TransparentTransformer.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/unary/TransparentTransformer.java index 5cec6149bcea..6b544c1025e5 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/unary/TransparentTransformer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/unary/TransparentTransformer.java @@ -16,10 +16,10 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.iotdb.db.query.udf.core.transformer.unary; +package org.apache.iotdb.db.mpp.transformation.dag.transformer.unary; import org.apache.iotdb.db.exception.query.QueryProcessException; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.io.IOException; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/unary/UnaryTransformer.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/unary/UnaryTransformer.java similarity index 90% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/unary/UnaryTransformer.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/unary/UnaryTransformer.java index c291d131bebd..7953b12a1cf3 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/transformer/unary/UnaryTransformer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/unary/UnaryTransformer.java @@ -17,11 +17,11 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.transformer.unary; +package org.apache.iotdb.db.mpp.transformation.dag.transformer.unary; import org.apache.iotdb.db.exception.query.QueryProcessException; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; -import org.apache.iotdb.db.query.udf.core.transformer.Transformer; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.dag.transformer.Transformer; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.io.IOException; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/executor/UDTFContext.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/udf/UDTFContext.java similarity index 89% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/executor/UDTFContext.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/udf/UDTFContext.java index 9350376708d4..1876756d8509 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/executor/UDTFContext.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/udf/UDTFContext.java @@ -17,11 +17,11 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.executor; +package org.apache.iotdb.db.mpp.transformation.dag.udf; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.ResultColumn; -import org.apache.iotdb.db.query.expression.multi.FunctionExpression; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ResultColumn; +import org.apache.iotdb.db.mpp.plan.expression.multi.FunctionExpression; import org.apache.iotdb.db.query.udf.service.UDFClassLoaderManager; import java.time.ZoneId; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/executor/UDTFExecutor.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/udf/UDTFExecutor.java similarity index 54% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/executor/UDTFExecutor.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/udf/UDTFExecutor.java index b8f697f6b77b..dd03c7e06c5c 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/executor/UDTFExecutor.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/udf/UDTFExecutor.java @@ -17,19 +17,16 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.executor; - -import org.apache.iotdb.db.exception.query.QueryProcessException; -import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.multi.FunctionExpression; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.access.RowWindow; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.datastructure.tv.ElasticSerializableTVList; +package org.apache.iotdb.db.mpp.transformation.dag.udf; + +import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.access.RowWindow; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.db.mpp.transformation.datastructure.tv.ElasticSerializableTVList; import org.apache.iotdb.db.query.udf.service.UDFRegistrationService; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; @@ -37,57 +34,36 @@ import org.slf4j.LoggerFactory; import java.time.ZoneId; +import java.util.List; import java.util.Map; public class UDTFExecutor { private static final Logger LOGGER = LoggerFactory.getLogger(UDTFExecutor.class); - protected final FunctionExpression expression; + protected final String functionName; protected final UDTFConfigurations configurations; protected UDTF udtf; protected ElasticSerializableTVList collector; - public UDTFExecutor(FunctionExpression expression, ZoneId zoneId) { - this.expression = expression; + public UDTFExecutor(String functionName, ZoneId zoneId) { + this.functionName = functionName; configurations = new UDTFConfigurations(zoneId); } - public void beforeStart(long queryId, float collectorMemoryBudgetInMB, TypeProvider typeProvider) - throws QueryProcessException { - udtf = (UDTF) UDFRegistrationService.getInstance().reflect(expression); - - UDFParameters parameters = new UDFParameters(expression, typeProvider); - - try { - udtf.validate(new UDFParameterValidator(parameters)); - } catch (Exception e) { - onError("validate(UDFParameterValidator)", e); - } - - try { - udtf.beforeStart(parameters, configurations); - } catch (Exception e) { - onError("beforeStart(UDFParameters, UDTFConfigurations)", e); - } - configurations.check(); - - collector = - ElasticSerializableTVList.newElasticSerializableTVList( - configurations.getOutputDataType(), queryId, collectorMemoryBudgetInMB, 1); - } - - // TODO: remove it after MPP finished - @Deprecated public void beforeStart( long queryId, float collectorMemoryBudgetInMB, - Map expressionDataTypeMap) - throws QueryProcessException { - udtf = (UDTF) UDFRegistrationService.getInstance().reflect(expression); + List childExpressions, + List maybeTimeSeriesPaths, + List childExpressionDataTypes, + Map attributes) { + udtf = (UDTF) UDFRegistrationService.getInstance().reflect(functionName); - UDFParameters parameters = new UDFParameters(expression, expressionDataTypeMap); + final UDFParameters parameters = + new UDFParameters( + childExpressions, maybeTimeSeriesPaths, childExpressionDataTypes, attributes); try { udtf.validate(new UDFParameterValidator(parameters)); @@ -107,7 +83,7 @@ public void beforeStart( configurations.getOutputDataType(), queryId, collectorMemoryBudgetInMB, 1); } - public void execute(Row row, boolean isCurrentRowNull) throws QueryProcessException { + public void execute(Row row, boolean isCurrentRowNull) { try { if (isCurrentRowNull) { // A null row will never trigger any UDF computing @@ -120,7 +96,7 @@ public void execute(Row row, boolean isCurrentRowNull) throws QueryProcessExcept } } - public void execute(RowWindow rowWindow) throws QueryProcessException { + public void execute(RowWindow rowWindow) { try { udtf.transform(rowWindow, collector); } catch (Exception e) { @@ -128,7 +104,7 @@ public void execute(RowWindow rowWindow) throws QueryProcessException { } } - public void terminate() throws QueryProcessException { + public void terminate() { try { udtf.terminate(collector); } catch (Exception e) { @@ -142,18 +118,14 @@ public void beforeDestroy() { } } - private void onError(String methodName, Exception e) throws QueryProcessException { + private void onError(String methodName, Exception e) { LOGGER.warn("Error occurred during executing UDTF", e); - throw new QueryProcessException( + throw new RuntimeException( String.format( "Error occurred during executing UDTF#%s: %s", methodName, System.lineSeparator()) + e); } - public FunctionExpression getExpression() { - return expression; - } - public UDTFConfigurations getConfigurations() { return configurations; } diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/executor/UDTFTypeInferrer.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/udf/UDTFTypeInferrer.java similarity index 67% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/executor/UDTFTypeInferrer.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/udf/UDTFTypeInferrer.java index ca7233906a54..4e3342c20294 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/executor/UDTFTypeInferrer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/udf/UDTFTypeInferrer.java @@ -17,15 +17,14 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.executor; +package org.apache.iotdb.db.mpp.transformation.dag.udf; +import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; import org.apache.iotdb.db.exception.sql.SemanticException; -import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider; -import org.apache.iotdb.db.query.expression.multi.FunctionExpression; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; import org.apache.iotdb.db.query.udf.service.UDFRegistrationService; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; @@ -33,22 +32,30 @@ import org.slf4j.LoggerFactory; import java.time.ZoneId; +import java.util.List; +import java.util.Map; public class UDTFTypeInferrer { private static final Logger LOGGER = LoggerFactory.getLogger(UDTFTypeInferrer.class); - protected final FunctionExpression expression; + protected final String functionName; - public UDTFTypeInferrer(FunctionExpression expression) { - this.expression = expression; + public UDTFTypeInferrer(String functionName) { + this.functionName = functionName; } - public TSDataType inferOutputType(TypeProvider typeProvider) { + public TSDataType inferOutputType( + List childExpressions, + List maybeTimeSeriesPaths, + List childExpressionDataTypes, + Map attributes) { try { - UDTF udtf = (UDTF) UDFRegistrationService.getInstance().reflect(expression); + UDTF udtf = (UDTF) UDFRegistrationService.getInstance().reflect(functionName); - UDFParameters parameters = new UDFParameters(expression, typeProvider); + UDFParameters parameters = + new UDFParameters( + childExpressions, maybeTimeSeriesPaths, childExpressionDataTypes, attributes); udtf.validate(new UDFParameterValidator(parameters)); // use ZoneId.systemDefault() because UDF's data type is ZoneId independent diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/InputRowUtils.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/util/InputRowUtils.java similarity index 97% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/InputRowUtils.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/util/InputRowUtils.java index 5a56256944dd..007b8f839709 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/InputRowUtils.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/util/InputRowUtils.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.iotdb.db.query.udf.core.layer; +package org.apache.iotdb.db.mpp.transformation.dag.util; import org.apache.iotdb.db.query.dataset.IUDFInputDataSet; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/LayerCacheUtils.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/util/LayerCacheUtils.java similarity index 91% rename from server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/LayerCacheUtils.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/util/LayerCacheUtils.java index ab63797f922d..fdef1eb90314 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/core/layer/LayerCacheUtils.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/util/LayerCacheUtils.java @@ -17,13 +17,13 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.core.layer; +package org.apache.iotdb.db.mpp.transformation.dag.util; import org.apache.iotdb.db.exception.query.QueryProcessException; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.datastructure.row.ElasticSerializableRowRecordList; +import org.apache.iotdb.db.mpp.transformation.datastructure.tv.ElasticSerializableTVList; import org.apache.iotdb.db.query.dataset.IUDFInputDataSet; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; -import org.apache.iotdb.db.query.udf.datastructure.row.ElasticSerializableRowRecordList; -import org.apache.iotdb.db.query.udf.datastructure.tv.ElasticSerializableTVList; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import java.io.IOException; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/datastructure/Cache.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/datastructure/Cache.java similarity index 97% rename from server/src/main/java/org/apache/iotdb/db/query/udf/datastructure/Cache.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/datastructure/Cache.java index 3194b821e8d6..c70f5446bb5d 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/datastructure/Cache.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/datastructure/Cache.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.datastructure; +package org.apache.iotdb.db.mpp.transformation.datastructure; /** Note: It's not thread safe. */ public abstract class Cache { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/datastructure/SerializableList.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/datastructure/SerializableList.java similarity index 97% rename from server/src/main/java/org/apache/iotdb/db/query/udf/datastructure/SerializableList.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/datastructure/SerializableList.java index 7ab360be2092..9ab4449a69cb 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/datastructure/SerializableList.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/datastructure/SerializableList.java @@ -17,11 +17,11 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.datastructure; +package org.apache.iotdb.db.mpp.transformation.datastructure; import org.apache.iotdb.commons.file.SystemFileFactory; import org.apache.iotdb.db.conf.IoTDBDescriptor; -import org.apache.iotdb.db.query.udf.service.TemporaryQueryDataFileService; +import org.apache.iotdb.db.service.TemporaryQueryDataFileService; import org.apache.iotdb.tsfile.utils.PublicBAOS; import java.io.IOException; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/datastructure/row/ElasticSerializableRowRecordList.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/datastructure/row/ElasticSerializableRowRecordList.java similarity index 93% rename from server/src/main/java/org/apache/iotdb/db/query/udf/datastructure/row/ElasticSerializableRowRecordList.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/datastructure/row/ElasticSerializableRowRecordList.java index d7d9ef6407d8..0d57428ea280 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/datastructure/row/ElasticSerializableRowRecordList.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/datastructure/row/ElasticSerializableRowRecordList.java @@ -17,11 +17,12 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.datastructure.row; +package org.apache.iotdb.db.mpp.transformation.datastructure.row; import org.apache.iotdb.db.exception.query.QueryProcessException; -import org.apache.iotdb.db.query.udf.core.layer.InputRowUtils; -import org.apache.iotdb.db.query.udf.datastructure.Cache; +import org.apache.iotdb.db.mpp.transformation.dag.util.InputRowUtils; +import org.apache.iotdb.db.mpp.transformation.datastructure.Cache; +import org.apache.iotdb.db.mpp.transformation.datastructure.SerializableList; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.utils.Binary; import org.apache.iotdb.tsfile.utils.BitMap; @@ -30,8 +31,6 @@ import java.util.ArrayList; import java.util.List; -import static org.apache.iotdb.db.query.udf.datastructure.SerializableList.INITIAL_BYTE_ARRAY_LENGTH_FOR_MEMORY_CONTROL; - /** An elastic list of records that implements memory control using LRU strategy. */ public class ElasticSerializableRowRecordList { @@ -73,7 +72,9 @@ public ElasticSerializableRowRecordList( this.memoryLimitInMB = memoryLimitInMB; int allocatableCapacity = SerializableRowRecordList.calculateCapacity( - dataTypes, memoryLimitInMB, INITIAL_BYTE_ARRAY_LENGTH_FOR_MEMORY_CONTROL); + dataTypes, + memoryLimitInMB, + SerializableList.INITIAL_BYTE_ARRAY_LENGTH_FOR_MEMORY_CONTROL); internalRowRecordListCapacity = allocatableCapacity / numCacheBlock; if (internalRowRecordListCapacity == 0) { numCacheBlock = 1; @@ -102,7 +103,7 @@ public ElasticSerializableRowRecordList( indexListOfTextFields[fieldIndex++] = i; } } - byteArrayLengthForMemoryControl = INITIAL_BYTE_ARRAY_LENGTH_FOR_MEMORY_CONTROL; + byteArrayLengthForMemoryControl = SerializableList.INITIAL_BYTE_ARRAY_LENGTH_FOR_MEMORY_CONTROL; totalByteArrayLengthLimit = 0; totalByteArrayLength = 0; } @@ -222,10 +223,10 @@ protected void checkMemoryUsage() throws IOException, QueryProcessException { ((totalByteArrayLength - totalByteArrayLengthLimit) / size / indexListOfTextFields.length - / INITIAL_BYTE_ARRAY_LENGTH_FOR_MEMORY_CONTROL); + / SerializableList.INITIAL_BYTE_ARRAY_LENGTH_FOR_MEMORY_CONTROL); newByteArrayLengthForMemoryControl = byteArrayLengthForMemoryControl - + 2 * (delta + 1) * INITIAL_BYTE_ARRAY_LENGTH_FOR_MEMORY_CONTROL; + + 2 * (delta + 1) * SerializableList.INITIAL_BYTE_ARRAY_LENGTH_FOR_MEMORY_CONTROL; newInternalTVListCapacity = SerializableRowRecordList.calculateCapacity( dataTypes, memoryLimitInMB, newByteArrayLengthForMemoryControl) diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/datastructure/row/SerializableRowRecordList.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/datastructure/row/SerializableRowRecordList.java similarity index 98% rename from server/src/main/java/org/apache/iotdb/db/query/udf/datastructure/row/SerializableRowRecordList.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/datastructure/row/SerializableRowRecordList.java index f49e449461a6..b13096659f51 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/datastructure/row/SerializableRowRecordList.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/datastructure/row/SerializableRowRecordList.java @@ -17,10 +17,10 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.datastructure.row; +package org.apache.iotdb.db.mpp.transformation.datastructure.row; import org.apache.iotdb.db.exception.query.QueryProcessException; -import org.apache.iotdb.db.query.udf.datastructure.SerializableList; +import org.apache.iotdb.db.mpp.transformation.datastructure.SerializableList; import org.apache.iotdb.tsfile.exception.write.UnSupportedDataTypeException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.utils.Binary; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/datastructure/tv/ElasticSerializableBinaryTVList.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/datastructure/tv/ElasticSerializableBinaryTVList.java similarity index 83% rename from server/src/main/java/org/apache/iotdb/db/query/udf/datastructure/tv/ElasticSerializableBinaryTVList.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/datastructure/tv/ElasticSerializableBinaryTVList.java index 0e481b7a5f55..29f507aad1c6 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/datastructure/tv/ElasticSerializableBinaryTVList.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/datastructure/tv/ElasticSerializableBinaryTVList.java @@ -17,16 +17,14 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.datastructure.tv; +package org.apache.iotdb.db.mpp.transformation.datastructure.tv; -import org.apache.iotdb.db.exception.query.QueryProcessException; +import org.apache.iotdb.db.mpp.transformation.datastructure.SerializableList; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.utils.Binary; import java.io.IOException; -import static org.apache.iotdb.db.query.udf.datastructure.SerializableList.INITIAL_BYTE_ARRAY_LENGTH_FOR_MEMORY_CONTROL; - public class ElasticSerializableBinaryTVList extends ElasticSerializableTVList { protected static final int MEMORY_CHECK_THRESHOLD = 1000; @@ -36,16 +34,15 @@ public class ElasticSerializableBinaryTVList extends ElasticSerializableTVList { protected long totalByteArrayLengthLimit; protected long totalByteArrayLength; - public ElasticSerializableBinaryTVList(long queryId, float memoryLimitInMB, int cacheSize) - throws QueryProcessException { + public ElasticSerializableBinaryTVList(long queryId, float memoryLimitInMB, int cacheSize) { super(TSDataType.TEXT, queryId, memoryLimitInMB, cacheSize); - byteArrayLengthForMemoryControl = INITIAL_BYTE_ARRAY_LENGTH_FOR_MEMORY_CONTROL; + byteArrayLengthForMemoryControl = SerializableList.INITIAL_BYTE_ARRAY_LENGTH_FOR_MEMORY_CONTROL; totalByteArrayLengthLimit = 0; totalByteArrayLength = 0; } @Override - public void putBinary(long timestamp, Binary value) throws IOException, QueryProcessException { + public void putBinary(long timestamp, Binary value) throws IOException { super.putBinary(timestamp, value); totalByteArrayLengthLimit += byteArrayLengthForMemoryControl; totalByteArrayLength += value.getLength(); @@ -53,7 +50,7 @@ public void putBinary(long timestamp, Binary value) throws IOException, QueryPro } @Override - public void putString(long timestamp, String value) throws IOException, QueryProcessException { + public void putString(long timestamp, String value) throws IOException { Binary binary = Binary.valueOf(value); super.putBinary(timestamp, binary); totalByteArrayLengthLimit += byteArrayLengthForMemoryControl; @@ -61,13 +58,13 @@ public void putString(long timestamp, String value) throws IOException, QueryPro checkMemoryUsage(); } - protected void checkMemoryUsage() throws IOException, QueryProcessException { + protected void checkMemoryUsage() throws IOException { if (size % MEMORY_CHECK_THRESHOLD != 0 || totalByteArrayLength <= totalByteArrayLengthLimit) { return; } int newByteArrayLengthForMemoryControl = byteArrayLengthForMemoryControl; - while (newByteArrayLengthForMemoryControl * size < totalByteArrayLength) { + while ((long) newByteArrayLengthForMemoryControl * size < totalByteArrayLength) { newByteArrayLengthForMemoryControl *= 2; } int newInternalTVListCapacity = @@ -84,10 +81,10 @@ protected void checkMemoryUsage() throws IOException, QueryProcessException { (int) ((totalByteArrayLength - totalByteArrayLengthLimit) / size - / INITIAL_BYTE_ARRAY_LENGTH_FOR_MEMORY_CONTROL); + / SerializableList.INITIAL_BYTE_ARRAY_LENGTH_FOR_MEMORY_CONTROL); newByteArrayLengthForMemoryControl = byteArrayLengthForMemoryControl - + 2 * (delta + 1) * INITIAL_BYTE_ARRAY_LENGTH_FOR_MEMORY_CONTROL; + + 2 * (delta + 1) * SerializableList.INITIAL_BYTE_ARRAY_LENGTH_FOR_MEMORY_CONTROL; newInternalTVListCapacity = SerializableBinaryTVList.calculateCapacity( memoryLimitInMB, newByteArrayLengthForMemoryControl) @@ -98,12 +95,11 @@ protected void checkMemoryUsage() throws IOException, QueryProcessException { return; } - throw new QueryProcessException("Memory is not enough for current query."); + throw new RuntimeException("Memory is not enough for current query."); } protected void applyNewMemoryControlParameters( - int newByteArrayLengthForMemoryControl, int newInternalTVListCapacity) - throws IOException, QueryProcessException { + int newByteArrayLengthForMemoryControl, int newInternalTVListCapacity) throws IOException { ElasticSerializableTVList newElasticSerializableTVList = new ElasticSerializableTVList( TSDataType.TEXT, queryId, memoryLimitInMB, newInternalTVListCapacity, cacheSize); diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/datastructure/tv/ElasticSerializableTVList.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/datastructure/tv/ElasticSerializableTVList.java similarity index 93% rename from server/src/main/java/org/apache/iotdb/db/query/udf/datastructure/tv/ElasticSerializableTVList.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/datastructure/tv/ElasticSerializableTVList.java index adf9abc09ac5..91fa3de5085d 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/datastructure/tv/ElasticSerializableTVList.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/datastructure/tv/ElasticSerializableTVList.java @@ -17,12 +17,12 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.datastructure.tv; +package org.apache.iotdb.db.mpp.transformation.datastructure.tv; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; import org.apache.iotdb.db.exception.query.QueryProcessException; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; -import org.apache.iotdb.db.query.udf.datastructure.Cache; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.datastructure.Cache; import org.apache.iotdb.tsfile.exception.write.UnSupportedDataTypeException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.read.common.BatchData; @@ -36,12 +36,10 @@ public class ElasticSerializableTVList implements PointCollector { public static ElasticSerializableTVList newElasticSerializableTVList( - TSDataType dataType, long queryId, float memoryLimitInMB, int cacheSize) - throws QueryProcessException { - if (dataType.equals(TSDataType.TEXT)) { - return new ElasticSerializableBinaryTVList(queryId, memoryLimitInMB, cacheSize); - } - return new ElasticSerializableTVList(dataType, queryId, memoryLimitInMB, cacheSize); + TSDataType dataType, long queryId, float memoryLimitInMB, int cacheSize) { + return dataType.equals(TSDataType.TEXT) + ? new ElasticSerializableBinaryTVList(queryId, memoryLimitInMB, cacheSize) + : new ElasticSerializableTVList(dataType, queryId, memoryLimitInMB, cacheSize); } protected TSDataType dataType; @@ -62,8 +60,7 @@ public static ElasticSerializableTVList newElasticSerializableTVList( protected int evictionUpperBound; protected ElasticSerializableTVList( - TSDataType dataType, long queryId, float memoryLimitInMB, int cacheSize) - throws QueryProcessException { + TSDataType dataType, long queryId, float memoryLimitInMB, int cacheSize) { this.dataType = dataType; this.queryId = queryId; this.memoryLimitInMB = memoryLimitInMB; @@ -218,20 +215,20 @@ public void putBoolean(long timestamp, boolean value) throws IOException { } @Override - public void putBinary(long timestamp, Binary value) throws IOException, QueryProcessException { + public void putBinary(long timestamp, Binary value) throws IOException { checkExpansion(); cache.get(size / internalTVListCapacity).putBinary(timestamp, value); ++size; } @Override - public void putString(long timestamp, String value) throws IOException, QueryProcessException { + public void putString(long timestamp, String value) throws IOException { checkExpansion(); cache.get(size / internalTVListCapacity).putBinary(timestamp, Binary.valueOf(value)); ++size; } - public void putNull(long timestamp) throws IOException, QueryProcessException { + public void putNull(long timestamp) throws IOException { switch (dataType) { case INT32: putInt(timestamp, 0); diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/datastructure/tv/SerializableBinaryTVList.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/datastructure/tv/SerializableBinaryTVList.java similarity index 98% rename from server/src/main/java/org/apache/iotdb/db/query/udf/datastructure/tv/SerializableBinaryTVList.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/datastructure/tv/SerializableBinaryTVList.java index 80f3fc5d0b57..b03467f12ca7 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/datastructure/tv/SerializableBinaryTVList.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/datastructure/tv/SerializableBinaryTVList.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.datastructure.tv; +package org.apache.iotdb.db.mpp.transformation.datastructure.tv; import org.apache.iotdb.tsfile.common.conf.TSFileConfig; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/datastructure/tv/SerializableBooleanTVList.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/datastructure/tv/SerializableBooleanTVList.java similarity index 97% rename from server/src/main/java/org/apache/iotdb/db/query/udf/datastructure/tv/SerializableBooleanTVList.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/datastructure/tv/SerializableBooleanTVList.java index b19246e074ff..2cf5e22637f2 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/datastructure/tv/SerializableBooleanTVList.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/datastructure/tv/SerializableBooleanTVList.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.datastructure.tv; +package org.apache.iotdb.db.mpp.transformation.datastructure.tv; import org.apache.iotdb.tsfile.common.conf.TSFileConfig; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/datastructure/tv/SerializableDoubleTVList.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/datastructure/tv/SerializableDoubleTVList.java similarity index 97% rename from server/src/main/java/org/apache/iotdb/db/query/udf/datastructure/tv/SerializableDoubleTVList.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/datastructure/tv/SerializableDoubleTVList.java index f9d28856c3c6..1f1675e81fdf 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/datastructure/tv/SerializableDoubleTVList.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/datastructure/tv/SerializableDoubleTVList.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.datastructure.tv; +package org.apache.iotdb.db.mpp.transformation.datastructure.tv; import org.apache.iotdb.tsfile.common.conf.TSFileConfig; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/datastructure/tv/SerializableFloatTVList.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/datastructure/tv/SerializableFloatTVList.java similarity index 97% rename from server/src/main/java/org/apache/iotdb/db/query/udf/datastructure/tv/SerializableFloatTVList.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/datastructure/tv/SerializableFloatTVList.java index 5415ad6b5646..540749bb2d42 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/datastructure/tv/SerializableFloatTVList.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/datastructure/tv/SerializableFloatTVList.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.datastructure.tv; +package org.apache.iotdb.db.mpp.transformation.datastructure.tv; import org.apache.iotdb.tsfile.common.conf.TSFileConfig; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/datastructure/tv/SerializableIntTVList.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/datastructure/tv/SerializableIntTVList.java similarity index 97% rename from server/src/main/java/org/apache/iotdb/db/query/udf/datastructure/tv/SerializableIntTVList.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/datastructure/tv/SerializableIntTVList.java index 86a0106aaae1..21894d55ba48 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/datastructure/tv/SerializableIntTVList.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/datastructure/tv/SerializableIntTVList.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.datastructure.tv; +package org.apache.iotdb.db.mpp.transformation.datastructure.tv; import org.apache.iotdb.tsfile.common.conf.TSFileConfig; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/datastructure/tv/SerializableLongTVList.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/datastructure/tv/SerializableLongTVList.java similarity index 97% rename from server/src/main/java/org/apache/iotdb/db/query/udf/datastructure/tv/SerializableLongTVList.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/datastructure/tv/SerializableLongTVList.java index e3300b42b031..60fb06037c8b 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/datastructure/tv/SerializableLongTVList.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/datastructure/tv/SerializableLongTVList.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.datastructure.tv; +package org.apache.iotdb.db.mpp.transformation.datastructure.tv; import org.apache.iotdb.tsfile.common.conf.TSFileConfig; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/datastructure/tv/SerializableTVList.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/datastructure/tv/SerializableTVList.java similarity index 90% rename from server/src/main/java/org/apache/iotdb/db/query/udf/datastructure/tv/SerializableTVList.java rename to server/src/main/java/org/apache/iotdb/db/mpp/transformation/datastructure/tv/SerializableTVList.java index edbee1c376db..a031be2a2bfb 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/datastructure/tv/SerializableTVList.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/datastructure/tv/SerializableTVList.java @@ -17,10 +17,9 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.datastructure.tv; +package org.apache.iotdb.db.mpp.transformation.datastructure.tv; -import org.apache.iotdb.db.exception.query.QueryProcessException; -import org.apache.iotdb.db.query.udf.datastructure.SerializableList; +import org.apache.iotdb.db.mpp.transformation.datastructure.SerializableList; import org.apache.iotdb.tsfile.exception.write.UnSupportedDataTypeException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.read.common.BatchData; @@ -47,8 +46,7 @@ public static SerializableTVList newSerializableTVList(TSDataType dataType, long } } - protected static int calculateCapacity(TSDataType dataType, float memoryLimitInMB) - throws QueryProcessException { + protected static int calculateCapacity(TSDataType dataType, float memoryLimitInMB) { int size; switch (dataType) { case INT32: @@ -76,7 +74,7 @@ protected static int calculateCapacity(TSDataType dataType, float memoryLimitInM } if (size <= 0) { - throw new QueryProcessException("Memory is not enough for current query."); + throw new RuntimeException("Memory is not enough for current query."); } return size; } diff --git a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/InfluxFunction.java b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/InfluxFunction.java index 3c800af52504..2904d1d2209b 100644 --- a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/InfluxFunction.java +++ b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/InfluxFunction.java @@ -18,8 +18,8 @@ */ package org.apache.iotdb.db.protocol.influxdb.function; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.leaf.TimeSeriesOperand; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand; import java.util.List; diff --git a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/InfluxFunctionFactory.java b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/InfluxFunctionFactory.java index 8c20c80a910b..9310990b4627 100644 --- a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/InfluxFunctionFactory.java +++ b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/InfluxFunctionFactory.java @@ -18,6 +18,7 @@ */ package org.apache.iotdb.db.protocol.influxdb.function; +import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.db.protocol.influxdb.constant.InfluxSQLConstant; import org.apache.iotdb.db.protocol.influxdb.function.aggregator.InfluxCountFunction; import org.apache.iotdb.db.protocol.influxdb.function.aggregator.InfluxMeanFunction; @@ -30,7 +31,6 @@ import org.apache.iotdb.db.protocol.influxdb.function.selector.InfluxLastFunction; import org.apache.iotdb.db.protocol.influxdb.function.selector.InfluxMaxFunction; import org.apache.iotdb.db.protocol.influxdb.function.selector.InfluxMinFunction; -import org.apache.iotdb.db.query.expression.Expression; import java.util.List; diff --git a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/aggregator/InfluxAggregator.java b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/aggregator/InfluxAggregator.java index 5d82ad966d6c..458f378053bc 100644 --- a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/aggregator/InfluxAggregator.java +++ b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/aggregator/InfluxAggregator.java @@ -19,9 +19,9 @@ package org.apache.iotdb.db.protocol.influxdb.function.aggregator; +import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.db.protocol.influxdb.function.InfluxFunction; import org.apache.iotdb.db.protocol.influxdb.function.InfluxFunctionValue; -import org.apache.iotdb.db.query.expression.Expression; import java.util.List; diff --git a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/aggregator/InfluxCountFunction.java b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/aggregator/InfluxCountFunction.java index f6acb4287c4a..7cbc59971fd7 100644 --- a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/aggregator/InfluxCountFunction.java +++ b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/aggregator/InfluxCountFunction.java @@ -19,9 +19,9 @@ package org.apache.iotdb.db.protocol.influxdb.function.aggregator; +import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.db.protocol.influxdb.constant.InfluxSQLConstant; import org.apache.iotdb.db.protocol.influxdb.function.InfluxFunctionValue; -import org.apache.iotdb.db.query.expression.Expression; import java.util.List; diff --git a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/aggregator/InfluxMeanFunction.java b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/aggregator/InfluxMeanFunction.java index 8a5a0c693046..fb1b936e2a07 100644 --- a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/aggregator/InfluxMeanFunction.java +++ b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/aggregator/InfluxMeanFunction.java @@ -19,9 +19,9 @@ package org.apache.iotdb.db.protocol.influxdb.function.aggregator; +import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.db.protocol.influxdb.constant.InfluxSQLConstant; import org.apache.iotdb.db.protocol.influxdb.function.InfluxFunctionValue; -import org.apache.iotdb.db.query.expression.Expression; import org.apache.iotdb.db.utils.MathUtils; import java.util.ArrayList; diff --git a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/aggregator/InfluxMedianFunction.java b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/aggregator/InfluxMedianFunction.java index 33d919a0b350..eea844f9c07e 100644 --- a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/aggregator/InfluxMedianFunction.java +++ b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/aggregator/InfluxMedianFunction.java @@ -19,9 +19,9 @@ package org.apache.iotdb.db.protocol.influxdb.function.aggregator; +import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.db.protocol.influxdb.constant.InfluxSQLConstant; import org.apache.iotdb.db.protocol.influxdb.function.InfluxFunctionValue; -import org.apache.iotdb.db.query.expression.Expression; import java.util.ArrayList; import java.util.Collections; diff --git a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/aggregator/InfluxModeFunction.java b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/aggregator/InfluxModeFunction.java index 48501047afa7..ff1f63c8e23f 100644 --- a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/aggregator/InfluxModeFunction.java +++ b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/aggregator/InfluxModeFunction.java @@ -19,9 +19,9 @@ package org.apache.iotdb.db.protocol.influxdb.function.aggregator; +import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.db.protocol.influxdb.constant.InfluxSQLConstant; import org.apache.iotdb.db.protocol.influxdb.function.InfluxFunctionValue; -import org.apache.iotdb.db.query.expression.Expression; import java.util.HashMap; import java.util.List; diff --git a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/aggregator/InfluxSpreadFunction.java b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/aggregator/InfluxSpreadFunction.java index b76ac494f643..a5cfcbde1e07 100644 --- a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/aggregator/InfluxSpreadFunction.java +++ b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/aggregator/InfluxSpreadFunction.java @@ -19,9 +19,9 @@ package org.apache.iotdb.db.protocol.influxdb.function.aggregator; +import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.db.protocol.influxdb.constant.InfluxSQLConstant; import org.apache.iotdb.db.protocol.influxdb.function.InfluxFunctionValue; -import org.apache.iotdb.db.query.expression.Expression; import java.util.List; diff --git a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/aggregator/InfluxStddevFunction.java b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/aggregator/InfluxStddevFunction.java index 614f4026d907..21790b617799 100644 --- a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/aggregator/InfluxStddevFunction.java +++ b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/aggregator/InfluxStddevFunction.java @@ -19,9 +19,9 @@ package org.apache.iotdb.db.protocol.influxdb.function.aggregator; +import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.db.protocol.influxdb.constant.InfluxSQLConstant; import org.apache.iotdb.db.protocol.influxdb.function.InfluxFunctionValue; -import org.apache.iotdb.db.query.expression.Expression; import org.apache.iotdb.db.utils.MathUtils; import java.util.ArrayList; diff --git a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/aggregator/InfluxSumFunction.java b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/aggregator/InfluxSumFunction.java index f175a1aaf204..a0ed37920c8b 100644 --- a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/aggregator/InfluxSumFunction.java +++ b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/aggregator/InfluxSumFunction.java @@ -19,9 +19,9 @@ package org.apache.iotdb.db.protocol.influxdb.function.aggregator; +import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.db.protocol.influxdb.constant.InfluxSQLConstant; import org.apache.iotdb.db.protocol.influxdb.function.InfluxFunctionValue; -import org.apache.iotdb.db.query.expression.Expression; import org.apache.iotdb.db.utils.MathUtils; import java.util.ArrayList; diff --git a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/selector/InfluxFirstFunction.java b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/selector/InfluxFirstFunction.java index bba41c0acc10..0599b8ddc510 100644 --- a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/selector/InfluxFirstFunction.java +++ b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/selector/InfluxFirstFunction.java @@ -19,9 +19,9 @@ package org.apache.iotdb.db.protocol.influxdb.function.selector; +import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.db.protocol.influxdb.constant.InfluxSQLConstant; import org.apache.iotdb.db.protocol.influxdb.function.InfluxFunctionValue; -import org.apache.iotdb.db.query.expression.Expression; import java.util.List; diff --git a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/selector/InfluxLastFunction.java b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/selector/InfluxLastFunction.java index 2674e0e8ec18..be9cce9119e8 100644 --- a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/selector/InfluxLastFunction.java +++ b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/selector/InfluxLastFunction.java @@ -19,9 +19,9 @@ package org.apache.iotdb.db.protocol.influxdb.function.selector; +import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.db.protocol.influxdb.constant.InfluxSQLConstant; import org.apache.iotdb.db.protocol.influxdb.function.InfluxFunctionValue; -import org.apache.iotdb.db.query.expression.Expression; import java.util.List; diff --git a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/selector/InfluxMaxFunction.java b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/selector/InfluxMaxFunction.java index 448955fde80b..c31f404b571a 100644 --- a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/selector/InfluxMaxFunction.java +++ b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/selector/InfluxMaxFunction.java @@ -19,9 +19,9 @@ package org.apache.iotdb.db.protocol.influxdb.function.selector; +import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.db.protocol.influxdb.constant.InfluxSQLConstant; import org.apache.iotdb.db.protocol.influxdb.function.InfluxFunctionValue; -import org.apache.iotdb.db.query.expression.Expression; import java.util.List; diff --git a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/selector/InfluxMinFunction.java b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/selector/InfluxMinFunction.java index 073a640d86a6..52d6177c5806 100644 --- a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/selector/InfluxMinFunction.java +++ b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/selector/InfluxMinFunction.java @@ -19,9 +19,9 @@ package org.apache.iotdb.db.protocol.influxdb.function.selector; +import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.db.protocol.influxdb.constant.InfluxSQLConstant; import org.apache.iotdb.db.protocol.influxdb.function.InfluxFunctionValue; -import org.apache.iotdb.db.query.expression.Expression; import java.util.List; diff --git a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/selector/InfluxSelector.java b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/selector/InfluxSelector.java index c4c9e3367d2d..e13013b3b612 100644 --- a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/selector/InfluxSelector.java +++ b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/function/selector/InfluxSelector.java @@ -19,9 +19,9 @@ package org.apache.iotdb.db.protocol.influxdb.function.selector; +import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.db.protocol.influxdb.function.InfluxFunction; import org.apache.iotdb.db.protocol.influxdb.function.InfluxFunctionValue; -import org.apache.iotdb.db.query.expression.Expression; import java.util.List; diff --git a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/handler/QueryHandler.java b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/handler/QueryHandler.java index 12f78ccde24f..e7b0881b6d76 100644 --- a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/handler/QueryHandler.java +++ b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/handler/QueryHandler.java @@ -23,6 +23,10 @@ import org.apache.iotdb.commons.exception.MetadataException; import org.apache.iotdb.db.exception.StorageEngineException; import org.apache.iotdb.db.exception.query.QueryProcessException; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ResultColumn; +import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand; +import org.apache.iotdb.db.mpp.plan.expression.multi.FunctionExpression; import org.apache.iotdb.db.protocol.influxdb.constant.InfluxConstant; import org.apache.iotdb.db.protocol.influxdb.constant.InfluxSQLConstant; import org.apache.iotdb.db.protocol.influxdb.function.InfluxFunction; @@ -46,10 +50,6 @@ import org.apache.iotdb.db.qp.physical.crud.QueryPlan; import org.apache.iotdb.db.query.context.QueryContext; import org.apache.iotdb.db.query.control.SessionManager; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.ResultColumn; -import org.apache.iotdb.db.query.expression.leaf.TimeSeriesOperand; -import org.apache.iotdb.db.query.expression.multi.FunctionExpression; import org.apache.iotdb.db.service.basic.ServiceProvider; import org.apache.iotdb.protocol.influxdb.rpc.thrift.InfluxQueryResultRsp; import org.apache.iotdb.rpc.RpcUtils; diff --git a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/operator/InfluxSelectComponent.java b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/operator/InfluxSelectComponent.java index 05e5c39fec31..79a1150f6633 100644 --- a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/operator/InfluxSelectComponent.java +++ b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/operator/InfluxSelectComponent.java @@ -18,11 +18,11 @@ */ package org.apache.iotdb.db.protocol.influxdb.operator; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ResultColumn; +import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand; +import org.apache.iotdb.db.mpp.plan.expression.multi.FunctionExpression; import org.apache.iotdb.db.protocol.influxdb.constant.InfluxSQLConstant; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.ResultColumn; -import org.apache.iotdb.db.query.expression.leaf.TimeSeriesOperand; -import org.apache.iotdb.db.query.expression.multi.FunctionExpression; import java.time.ZoneId; diff --git a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/sql/InfluxDBSqlVisitor.java b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/sql/InfluxDBSqlVisitor.java index b66e14ce4756..f413a4a4b37e 100644 --- a/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/sql/InfluxDBSqlVisitor.java +++ b/server/src/main/java/org/apache/iotdb/db/protocol/influxdb/sql/InfluxDBSqlVisitor.java @@ -19,6 +19,16 @@ package org.apache.iotdb.db.protocol.influxdb.sql; import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ResultColumn; +import org.apache.iotdb.db.mpp.plan.expression.binary.AdditionExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.DivisionExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.ModuloExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.MultiplicationExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.SubtractionExpression; +import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand; +import org.apache.iotdb.db.mpp.plan.expression.multi.FunctionExpression; +import org.apache.iotdb.db.mpp.plan.expression.unary.NegationExpression; import org.apache.iotdb.db.protocol.influxdb.operator.InfluxQueryOperator; import org.apache.iotdb.db.protocol.influxdb.operator.InfluxSelectComponent; import org.apache.iotdb.db.qp.constant.FilterConstant; @@ -31,16 +41,6 @@ import org.apache.iotdb.db.qp.sql.InfluxDBSqlParser; import org.apache.iotdb.db.qp.sql.InfluxDBSqlParserBaseVisitor; import org.apache.iotdb.db.qp.utils.DatetimeUtils; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.ResultColumn; -import org.apache.iotdb.db.query.expression.binary.AdditionExpression; -import org.apache.iotdb.db.query.expression.binary.DivisionExpression; -import org.apache.iotdb.db.query.expression.binary.ModuloExpression; -import org.apache.iotdb.db.query.expression.binary.MultiplicationExpression; -import org.apache.iotdb.db.query.expression.binary.SubtractionExpression; -import org.apache.iotdb.db.query.expression.leaf.TimeSeriesOperand; -import org.apache.iotdb.db.query.expression.multi.FunctionExpression; -import org.apache.iotdb.db.query.expression.unary.NegationExpression; public class InfluxDBSqlVisitor extends InfluxDBSqlParserBaseVisitor { diff --git a/server/src/main/java/org/apache/iotdb/db/protocol/rest/handler/QueryDataSetHandler.java b/server/src/main/java/org/apache/iotdb/db/protocol/rest/handler/QueryDataSetHandler.java index 6f68d4df19fc..f12c02e17987 100644 --- a/server/src/main/java/org/apache/iotdb/db/protocol/rest/handler/QueryDataSetHandler.java +++ b/server/src/main/java/org/apache/iotdb/db/protocol/rest/handler/QueryDataSetHandler.java @@ -17,6 +17,7 @@ package org.apache.iotdb.db.protocol.rest.handler; +import org.apache.iotdb.db.mpp.plan.expression.ResultColumn; import org.apache.iotdb.db.protocol.rest.model.ExecutionStatus; import org.apache.iotdb.db.qp.physical.PhysicalPlan; import org.apache.iotdb.db.qp.physical.crud.AggregationPlan; @@ -29,7 +30,6 @@ import org.apache.iotdb.db.query.dataset.ShowTimeseriesDataSet; import org.apache.iotdb.db.query.dataset.SingleDataSet; import org.apache.iotdb.db.query.dataset.groupby.GroupByLevelDataSet; -import org.apache.iotdb.db.query.expression.ResultColumn; import org.apache.iotdb.rpc.TSStatusCode; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.read.common.Field; diff --git a/server/src/main/java/org/apache/iotdb/db/qp/constant/FilterConstant.java b/server/src/main/java/org/apache/iotdb/db/qp/constant/FilterConstant.java index 2e4ebbe383c9..5a92fa4ecdd0 100644 --- a/server/src/main/java/org/apache/iotdb/db/qp/constant/FilterConstant.java +++ b/server/src/main/java/org/apache/iotdb/db/qp/constant/FilterConstant.java @@ -19,8 +19,8 @@ package org.apache.iotdb.db.qp.constant; +import org.apache.iotdb.db.mpp.plan.expression.ExpressionType; import org.apache.iotdb.db.qp.sql.SqlLexer; -import org.apache.iotdb.db.query.expression.ExpressionType; import java.util.EnumMap; import java.util.HashMap; diff --git a/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/AggregationQueryOperator.java b/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/AggregationQueryOperator.java index e5d80719ca35..8bd7d6a7a9e9 100644 --- a/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/AggregationQueryOperator.java +++ b/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/AggregationQueryOperator.java @@ -23,16 +23,16 @@ import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.exception.query.LogicalOperatorException; import org.apache.iotdb.db.exception.query.QueryProcessException; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ResultColumn; +import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand; +import org.apache.iotdb.db.mpp.plan.expression.multi.FunctionExpression; import org.apache.iotdb.db.qp.constant.SQLConstant; import org.apache.iotdb.db.qp.physical.PhysicalPlan; import org.apache.iotdb.db.qp.physical.crud.AggregationPlan; import org.apache.iotdb.db.qp.physical.crud.AlignByDevicePlan; import org.apache.iotdb.db.qp.physical.crud.QueryPlan; import org.apache.iotdb.db.qp.strategy.PhysicalGenerator; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.ResultColumn; -import org.apache.iotdb.db.query.expression.leaf.TimeSeriesOperand; -import org.apache.iotdb.db.query.expression.multi.FunctionExpression; import org.apache.iotdb.db.utils.SchemaUtils; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/LastQueryOperator.java b/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/LastQueryOperator.java index 63eddbe346ab..98ecd4c2202a 100644 --- a/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/LastQueryOperator.java +++ b/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/LastQueryOperator.java @@ -21,12 +21,12 @@ import org.apache.iotdb.db.exception.query.LogicalOperatorException; import org.apache.iotdb.db.exception.query.QueryProcessException; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ResultColumn; +import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand; import org.apache.iotdb.db.qp.physical.PhysicalPlan; import org.apache.iotdb.db.qp.physical.crud.LastQueryPlan; import org.apache.iotdb.db.qp.strategy.PhysicalGenerator; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.ResultColumn; -import org.apache.iotdb.db.query.expression.leaf.TimeSeriesOperand; public class LastQueryOperator extends QueryOperator { diff --git a/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/QueryOperator.java b/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/QueryOperator.java index 9d426332d3f4..3259f3ae6c64 100644 --- a/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/QueryOperator.java +++ b/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/QueryOperator.java @@ -25,6 +25,10 @@ import org.apache.iotdb.db.exception.query.QueryProcessException; import org.apache.iotdb.db.index.common.IndexType; import org.apache.iotdb.db.metadata.path.MeasurementPath; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ResultColumn; +import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand; +import org.apache.iotdb.db.mpp.plan.expression.multi.FunctionExpression; import org.apache.iotdb.db.qp.constant.SQLConstant; import org.apache.iotdb.db.qp.logical.Operator; import org.apache.iotdb.db.qp.physical.PhysicalPlan; @@ -34,10 +38,6 @@ import org.apache.iotdb.db.qp.physical.crud.QueryPlan; import org.apache.iotdb.db.qp.physical.crud.RawDataQueryPlan; import org.apache.iotdb.db.qp.strategy.PhysicalGenerator; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.ResultColumn; -import org.apache.iotdb.db.query.expression.leaf.TimeSeriesOperand; -import org.apache.iotdb.db.query.expression.multi.FunctionExpression; import org.apache.iotdb.db.service.IoTDB; import org.apache.iotdb.tsfile.exception.filter.QueryFilterOptimizationException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/SelectComponent.java b/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/SelectComponent.java index 19e2840224b6..2b4616278e5d 100644 --- a/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/SelectComponent.java +++ b/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/SelectComponent.java @@ -20,10 +20,10 @@ package org.apache.iotdb.db.qp.logical.crud; import org.apache.iotdb.commons.path.PartialPath; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.ResultColumn; -import org.apache.iotdb.db.query.expression.leaf.TimeSeriesOperand; -import org.apache.iotdb.db.query.expression.multi.FunctionExpression; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ResultColumn; +import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand; +import org.apache.iotdb.db.mpp.plan.expression.multi.FunctionExpression; import java.time.ZoneId; import java.util.ArrayList; diff --git a/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/SpecialClauseComponent.java b/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/SpecialClauseComponent.java index ec0c3a74def2..f9b411a1b8e3 100644 --- a/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/SpecialClauseComponent.java +++ b/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/SpecialClauseComponent.java @@ -19,8 +19,8 @@ package org.apache.iotdb.db.qp.logical.crud; +import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.db.qp.utils.GroupByLevelController; -import org.apache.iotdb.db.query.expression.Expression; import java.util.ArrayList; import java.util.List; diff --git a/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/UDAFQueryOperator.java b/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/UDAFQueryOperator.java index fd9888f36eaf..be2b53a8c9f0 100644 --- a/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/UDAFQueryOperator.java +++ b/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/UDAFQueryOperator.java @@ -20,14 +20,14 @@ import org.apache.iotdb.db.exception.query.LogicalOperatorException; import org.apache.iotdb.db.exception.query.QueryProcessException; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ResultColumn; +import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand; +import org.apache.iotdb.db.mpp.plan.expression.multi.FunctionExpression; import org.apache.iotdb.db.qp.physical.PhysicalPlan; import org.apache.iotdb.db.qp.physical.crud.AggregationPlan; import org.apache.iotdb.db.qp.physical.crud.UDAFPlan; import org.apache.iotdb.db.qp.strategy.PhysicalGenerator; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.ResultColumn; -import org.apache.iotdb.db.query.expression.leaf.TimeSeriesOperand; -import org.apache.iotdb.db.query.expression.multi.FunctionExpression; import java.util.ArrayList; import java.util.HashMap; diff --git a/server/src/main/java/org/apache/iotdb/db/qp/physical/crud/AggregationPlan.java b/server/src/main/java/org/apache/iotdb/db/qp/physical/crud/AggregationPlan.java index d0a87bf44d75..b8954ec81c00 100644 --- a/server/src/main/java/org/apache/iotdb/db/qp/physical/crud/AggregationPlan.java +++ b/server/src/main/java/org/apache/iotdb/db/qp/physical/crud/AggregationPlan.java @@ -20,10 +20,10 @@ import org.apache.iotdb.commons.exception.MetadataException; import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.db.mpp.plan.expression.ResultColumn; import org.apache.iotdb.db.qp.logical.Operator; import org.apache.iotdb.db.qp.utils.GroupByLevelController; import org.apache.iotdb.db.query.aggregation.AggregateResult; -import org.apache.iotdb.db.query.expression.ResultColumn; import org.apache.iotdb.db.utils.SchemaUtils; import org.apache.iotdb.rpc.RpcUtils; import org.apache.iotdb.rpc.TSStatusCode; diff --git a/server/src/main/java/org/apache/iotdb/db/qp/physical/crud/AlignByDevicePlan.java b/server/src/main/java/org/apache/iotdb/db/qp/physical/crud/AlignByDevicePlan.java index 35a22188e4f2..561aae819d10 100644 --- a/server/src/main/java/org/apache/iotdb/db/qp/physical/crud/AlignByDevicePlan.java +++ b/server/src/main/java/org/apache/iotdb/db/qp/physical/crud/AlignByDevicePlan.java @@ -20,12 +20,12 @@ import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.exception.query.QueryProcessException; +import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.db.qp.constant.SQLConstant; import org.apache.iotdb.db.qp.logical.Operator; import org.apache.iotdb.db.qp.logical.Operator.OperatorType; import org.apache.iotdb.db.qp.logical.crud.SpecialClauseComponent; import org.apache.iotdb.db.qp.strategy.PhysicalGenerator; -import org.apache.iotdb.db.query.expression.Expression; import org.apache.iotdb.rpc.RpcUtils; import org.apache.iotdb.rpc.TSStatusCode; import org.apache.iotdb.service.rpc.thrift.TSExecuteStatementResp; diff --git a/server/src/main/java/org/apache/iotdb/db/qp/physical/crud/LastQueryPlan.java b/server/src/main/java/org/apache/iotdb/db/qp/physical/crud/LastQueryPlan.java index b7a54911eb62..eea48cf4ed11 100644 --- a/server/src/main/java/org/apache/iotdb/db/qp/physical/crud/LastQueryPlan.java +++ b/server/src/main/java/org/apache/iotdb/db/qp/physical/crud/LastQueryPlan.java @@ -21,9 +21,9 @@ import org.apache.iotdb.commons.exception.MetadataException; import org.apache.iotdb.db.exception.query.QueryProcessException; +import org.apache.iotdb.db.mpp.plan.expression.ResultColumn; import org.apache.iotdb.db.qp.logical.Operator; import org.apache.iotdb.db.qp.strategy.PhysicalGenerator; -import org.apache.iotdb.db.query.expression.ResultColumn; import org.apache.iotdb.db.service.StaticResps; import org.apache.iotdb.service.rpc.thrift.TSExecuteStatementResp; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; diff --git a/server/src/main/java/org/apache/iotdb/db/qp/physical/crud/QueryPlan.java b/server/src/main/java/org/apache/iotdb/db/qp/physical/crud/QueryPlan.java index 0dc86af4fcf9..ba8b4ad5ba36 100644 --- a/server/src/main/java/org/apache/iotdb/db/qp/physical/crud/QueryPlan.java +++ b/server/src/main/java/org/apache/iotdb/db/qp/physical/crud/QueryPlan.java @@ -22,11 +22,11 @@ import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.exception.query.QueryProcessException; import org.apache.iotdb.db.metadata.path.MeasurementPath; +import org.apache.iotdb.db.mpp.plan.expression.ResultColumn; import org.apache.iotdb.db.qp.logical.Operator; import org.apache.iotdb.db.qp.logical.crud.SpecialClauseComponent; import org.apache.iotdb.db.qp.physical.PhysicalPlan; import org.apache.iotdb.db.qp.strategy.PhysicalGenerator; -import org.apache.iotdb.db.query.expression.ResultColumn; import org.apache.iotdb.db.service.IoTDB; import org.apache.iotdb.db.utils.SchemaUtils; import org.apache.iotdb.rpc.RpcUtils; diff --git a/server/src/main/java/org/apache/iotdb/db/qp/physical/crud/RawDataQueryPlan.java b/server/src/main/java/org/apache/iotdb/db/qp/physical/crud/RawDataQueryPlan.java index 2f9dce7afcf0..b6952e754bea 100644 --- a/server/src/main/java/org/apache/iotdb/db/qp/physical/crud/RawDataQueryPlan.java +++ b/server/src/main/java/org/apache/iotdb/db/qp/physical/crud/RawDataQueryPlan.java @@ -22,9 +22,9 @@ import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.exception.query.QueryProcessException; import org.apache.iotdb.db.metadata.path.AlignedPath; +import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.db.qp.logical.crud.SpecialClauseComponent; import org.apache.iotdb.db.qp.strategy.PhysicalGenerator; -import org.apache.iotdb.db.query.expression.Expression; import org.apache.iotdb.db.utils.SchemaUtils; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.read.common.Path; diff --git a/server/src/main/java/org/apache/iotdb/db/qp/physical/crud/UDAFPlan.java b/server/src/main/java/org/apache/iotdb/db/qp/physical/crud/UDAFPlan.java index 483e2cdb2394..7205fff53239 100644 --- a/server/src/main/java/org/apache/iotdb/db/qp/physical/crud/UDAFPlan.java +++ b/server/src/main/java/org/apache/iotdb/db/qp/physical/crud/UDAFPlan.java @@ -19,9 +19,9 @@ package org.apache.iotdb.db.qp.physical.crud; import org.apache.iotdb.commons.exception.MetadataException; +import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.db.qp.logical.Operator.OperatorType; import org.apache.iotdb.db.qp.strategy.PhysicalGenerator; -import org.apache.iotdb.db.query.expression.Expression; import org.apache.iotdb.service.rpc.thrift.TSExecuteStatementResp; import org.apache.thrift.TException; diff --git a/server/src/main/java/org/apache/iotdb/db/qp/physical/crud/UDFPlan.java b/server/src/main/java/org/apache/iotdb/db/qp/physical/crud/UDFPlan.java index a60640b1f1ac..9fb119f4ef79 100644 --- a/server/src/main/java/org/apache/iotdb/db/qp/physical/crud/UDFPlan.java +++ b/server/src/main/java/org/apache/iotdb/db/qp/physical/crud/UDFPlan.java @@ -19,7 +19,7 @@ package org.apache.iotdb.db.qp.physical.crud; -import org.apache.iotdb.db.query.expression.ResultColumn; +import org.apache.iotdb.db.mpp.plan.expression.ResultColumn; import java.util.List; diff --git a/server/src/main/java/org/apache/iotdb/db/qp/physical/crud/UDTFPlan.java b/server/src/main/java/org/apache/iotdb/db/qp/physical/crud/UDTFPlan.java index 5ee605a460e8..158e454c8d01 100644 --- a/server/src/main/java/org/apache/iotdb/db/qp/physical/crud/UDTFPlan.java +++ b/server/src/main/java/org/apache/iotdb/db/qp/physical/crud/UDTFPlan.java @@ -22,11 +22,11 @@ import org.apache.iotdb.commons.exception.MetadataException; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.metadata.path.MeasurementPath; +import org.apache.iotdb.db.mpp.plan.expression.ResultColumn; +import org.apache.iotdb.db.mpp.plan.expression.leaf.TimestampOperand; +import org.apache.iotdb.db.mpp.transformation.dag.udf.UDTFContext; import org.apache.iotdb.db.qp.logical.Operator; import org.apache.iotdb.db.qp.strategy.PhysicalGenerator; -import org.apache.iotdb.db.query.expression.ResultColumn; -import org.apache.iotdb.db.query.expression.leaf.TimestampOperand; -import org.apache.iotdb.db.query.udf.core.executor.UDTFContext; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.utils.Pair; diff --git a/server/src/main/java/org/apache/iotdb/db/qp/sql/IoTDBSqlVisitor.java b/server/src/main/java/org/apache/iotdb/db/qp/sql/IoTDBSqlVisitor.java index a81179f435aa..954d043a628d 100644 --- a/server/src/main/java/org/apache/iotdb/db/qp/sql/IoTDBSqlVisitor.java +++ b/server/src/main/java/org/apache/iotdb/db/qp/sql/IoTDBSqlVisitor.java @@ -24,6 +24,31 @@ import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.engine.trigger.executor.TriggerEvent; import org.apache.iotdb.db.exception.sql.SQLParserException; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ResultColumn; +import org.apache.iotdb.db.mpp.plan.expression.binary.AdditionExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.BinaryExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.DivisionExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.EqualToExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.GreaterEqualExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.GreaterThanExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.LessEqualExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.LessThanExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.LogicAndExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.LogicOrExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.ModuloExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.MultiplicationExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.NonEqualExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.SubtractionExpression; +import org.apache.iotdb.db.mpp.plan.expression.leaf.ConstantOperand; +import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand; +import org.apache.iotdb.db.mpp.plan.expression.leaf.TimestampOperand; +import org.apache.iotdb.db.mpp.plan.expression.multi.FunctionExpression; +import org.apache.iotdb.db.mpp.plan.expression.unary.InExpression; +import org.apache.iotdb.db.mpp.plan.expression.unary.LikeExpression; +import org.apache.iotdb.db.mpp.plan.expression.unary.LogicNotExpression; +import org.apache.iotdb.db.mpp.plan.expression.unary.NegationExpression; +import org.apache.iotdb.db.mpp.plan.expression.unary.RegularExpression; import org.apache.iotdb.db.qp.constant.FilterConstant; import org.apache.iotdb.db.qp.constant.FilterConstant.FilterType; import org.apache.iotdb.db.qp.constant.SQLConstant; @@ -126,31 +151,6 @@ import org.apache.iotdb.db.query.executor.fill.LinearFill; import org.apache.iotdb.db.query.executor.fill.PreviousFill; import org.apache.iotdb.db.query.executor.fill.ValueFill; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.ResultColumn; -import org.apache.iotdb.db.query.expression.binary.AdditionExpression; -import org.apache.iotdb.db.query.expression.binary.BinaryExpression; -import org.apache.iotdb.db.query.expression.binary.DivisionExpression; -import org.apache.iotdb.db.query.expression.binary.EqualToExpression; -import org.apache.iotdb.db.query.expression.binary.GreaterEqualExpression; -import org.apache.iotdb.db.query.expression.binary.GreaterThanExpression; -import org.apache.iotdb.db.query.expression.binary.LessEqualExpression; -import org.apache.iotdb.db.query.expression.binary.LessThanExpression; -import org.apache.iotdb.db.query.expression.binary.LogicAndExpression; -import org.apache.iotdb.db.query.expression.binary.LogicOrExpression; -import org.apache.iotdb.db.query.expression.binary.ModuloExpression; -import org.apache.iotdb.db.query.expression.binary.MultiplicationExpression; -import org.apache.iotdb.db.query.expression.binary.NonEqualExpression; -import org.apache.iotdb.db.query.expression.binary.SubtractionExpression; -import org.apache.iotdb.db.query.expression.leaf.ConstantOperand; -import org.apache.iotdb.db.query.expression.leaf.TimeSeriesOperand; -import org.apache.iotdb.db.query.expression.leaf.TimestampOperand; -import org.apache.iotdb.db.query.expression.multi.FunctionExpression; -import org.apache.iotdb.db.query.expression.unary.InExpression; -import org.apache.iotdb.db.query.expression.unary.LikeExpression; -import org.apache.iotdb.db.query.expression.unary.LogicNotExpression; -import org.apache.iotdb.db.query.expression.unary.NegationExpression; -import org.apache.iotdb.db.query.expression.unary.RegularExpression; import org.apache.iotdb.tsfile.common.conf.TSFileDescriptor; import org.apache.iotdb.tsfile.common.constant.TsFileConstant; import org.apache.iotdb.tsfile.file.metadata.enums.CompressionType; diff --git a/server/src/main/java/org/apache/iotdb/db/qp/strategy/LogicalGenerator.java b/server/src/main/java/org/apache/iotdb/db/qp/strategy/LogicalGenerator.java index 9e12f0655c6e..b7944c64eedb 100644 --- a/server/src/main/java/org/apache/iotdb/db/qp/strategy/LogicalGenerator.java +++ b/server/src/main/java/org/apache/iotdb/db/qp/strategy/LogicalGenerator.java @@ -22,6 +22,8 @@ import org.apache.iotdb.commons.exception.IllegalPathException; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.commons.utils.TestOnly; +import org.apache.iotdb.db.mpp.plan.expression.ResultColumn; +import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand; import org.apache.iotdb.db.qp.constant.FilterConstant.FilterType; import org.apache.iotdb.db.qp.logical.Operator; import org.apache.iotdb.db.qp.logical.crud.BasicFunctionOperator; @@ -34,8 +36,6 @@ import org.apache.iotdb.db.qp.sql.IoTDBSqlParser; import org.apache.iotdb.db.qp.sql.IoTDBSqlVisitor; import org.apache.iotdb.db.qp.sql.SqlLexer; -import org.apache.iotdb.db.query.expression.ResultColumn; -import org.apache.iotdb.db.query.expression.leaf.TimeSeriesOperand; import org.apache.iotdb.service.rpc.thrift.TSLastDataQueryReq; import org.apache.iotdb.service.rpc.thrift.TSRawDataQueryReq; diff --git a/server/src/main/java/org/apache/iotdb/db/qp/strategy/optimizer/ConcatPathOptimizer.java b/server/src/main/java/org/apache/iotdb/db/qp/strategy/optimizer/ConcatPathOptimizer.java index 46a37fe3e9d6..c25498042d92 100644 --- a/server/src/main/java/org/apache/iotdb/db/qp/strategy/optimizer/ConcatPathOptimizer.java +++ b/server/src/main/java/org/apache/iotdb/db/qp/strategy/optimizer/ConcatPathOptimizer.java @@ -26,6 +26,9 @@ import org.apache.iotdb.db.exception.query.PathNumOverLimitException; import org.apache.iotdb.db.exception.sql.SQLParserException; import org.apache.iotdb.db.metadata.path.MeasurementPath; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ResultColumn; +import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand; import org.apache.iotdb.db.qp.constant.FilterConstant.FilterType; import org.apache.iotdb.db.qp.constant.SQLConstant; import org.apache.iotdb.db.qp.logical.Operator; @@ -41,9 +44,6 @@ import org.apache.iotdb.db.qp.logical.crud.WhereComponent; import org.apache.iotdb.db.qp.utils.GroupByLevelController; import org.apache.iotdb.db.qp.utils.WildcardsRemover; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.ResultColumn; -import org.apache.iotdb.db.query.expression.leaf.TimeSeriesOperand; import org.apache.iotdb.db.service.IoTDB; import org.slf4j.Logger; diff --git a/server/src/main/java/org/apache/iotdb/db/qp/utils/DatetimeUtils.java b/server/src/main/java/org/apache/iotdb/db/qp/utils/DatetimeUtils.java index 9a1dd4fc6461..e77d67344b6c 100644 --- a/server/src/main/java/org/apache/iotdb/db/qp/utils/DatetimeUtils.java +++ b/server/src/main/java/org/apache/iotdb/db/qp/utils/DatetimeUtils.java @@ -20,7 +20,6 @@ import org.apache.iotdb.commons.utils.TestOnly; import org.apache.iotdb.db.conf.IoTDBDescriptor; -import org.apache.iotdb.db.exception.query.LogicalOperatorException; import org.apache.iotdb.db.query.control.SessionManager; import java.time.DateTimeException; @@ -450,13 +449,11 @@ private DatetimeUtils() { .appendOptional(ISO_OFFSET_DATE_TIME_WITH_DOT_WITH_SPACE_NS) .toFormatter(); - public static long convertDatetimeStrToLong(String str, ZoneId zoneId) - throws LogicalOperatorException { + public static long convertDatetimeStrToLong(String str, ZoneId zoneId) { return convertDatetimeStrToLong(str, toZoneOffset(zoneId), 0); } - public static long getInstantWithPrecision(String str, String timestampPrecision) - throws LogicalOperatorException { + public static long getInstantWithPrecision(String str, String timestampPrecision) { try { ZonedDateTime zonedDateTime = ZonedDateTime.parse(str, formatter); Instant instant = zonedDateTime.toInstant(); @@ -476,13 +473,12 @@ public static long getInstantWithPrecision(String str, String timestampPrecision } return instant.toEpochMilli(); } catch (DateTimeParseException e) { - throw new LogicalOperatorException(e.getMessage()); + throw new RuntimeException(e.getMessage()); } } /** convert date time string to millisecond, microsecond or nanosecond. */ - public static long convertDatetimeStrToLong(String str, ZoneOffset offset, int depth) - throws LogicalOperatorException { + public static long convertDatetimeStrToLong(String str, ZoneOffset offset, int depth) { String timestampPrecision = IoTDBDescriptor.getInstance().getConfig().getTimestampPrecision(); diff --git a/server/src/main/java/org/apache/iotdb/db/qp/utils/GroupByLevelController.java b/server/src/main/java/org/apache/iotdb/db/qp/utils/GroupByLevelController.java index c1fb4657f8d5..781838c88bab 100644 --- a/server/src/main/java/org/apache/iotdb/db/qp/utils/GroupByLevelController.java +++ b/server/src/main/java/org/apache/iotdb/db/qp/utils/GroupByLevelController.java @@ -22,10 +22,10 @@ import org.apache.iotdb.commons.conf.IoTDBConstant; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.exception.query.LogicalOptimizeException; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ResultColumn; +import org.apache.iotdb.db.mpp.plan.expression.multi.FunctionExpression; import org.apache.iotdb.db.qp.logical.crud.QueryOperator; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.ResultColumn; -import org.apache.iotdb.db.query.expression.multi.FunctionExpression; import org.apache.iotdb.tsfile.common.constant.TsFileConstant; import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils; diff --git a/server/src/main/java/org/apache/iotdb/db/qp/utils/WildcardsRemover.java b/server/src/main/java/org/apache/iotdb/db/qp/utils/WildcardsRemover.java index e0c22188f661..30cf3df9f35a 100644 --- a/server/src/main/java/org/apache/iotdb/db/qp/utils/WildcardsRemover.java +++ b/server/src/main/java/org/apache/iotdb/db/qp/utils/WildcardsRemover.java @@ -25,10 +25,10 @@ import org.apache.iotdb.db.exception.query.LogicalOptimizeException; import org.apache.iotdb.db.exception.query.PathNumOverLimitException; import org.apache.iotdb.db.metadata.path.MeasurementPath; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ResultColumn; import org.apache.iotdb.db.qp.logical.crud.QueryOperator; import org.apache.iotdb.db.qp.strategy.optimizer.ConcatPathOptimizer; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.ResultColumn; import org.apache.iotdb.db.service.IoTDB; import org.apache.iotdb.tsfile.utils.Pair; diff --git a/server/src/main/java/org/apache/iotdb/db/query/control/QueryResourceManager.java b/server/src/main/java/org/apache/iotdb/db/query/control/QueryResourceManager.java index 5dd6e0a648fa..2f436fd3926c 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/control/QueryResourceManager.java +++ b/server/src/main/java/org/apache/iotdb/db/query/control/QueryResourceManager.java @@ -27,7 +27,7 @@ import org.apache.iotdb.db.metadata.idtable.IDTable; import org.apache.iotdb.db.query.context.QueryContext; import org.apache.iotdb.db.query.externalsort.serialize.IExternalSortFileDeserializer; -import org.apache.iotdb.db.query.udf.service.TemporaryQueryDataFileService; +import org.apache.iotdb.db.service.TemporaryQueryDataFileService; import org.apache.iotdb.db.utils.QueryUtils; import org.apache.iotdb.tsfile.read.filter.basic.Filter; @@ -199,7 +199,7 @@ public QueryDataSource getQueryDataSource( * Whenever the jdbc request is closed normally or abnormally, this method must be invoked. All * query tokens created by this jdbc request must be cleared. */ - @SuppressWarnings("squid:S3776") // Suppress high Cognitive Complexity warning + // Suppress high Cognitive Complexity warning public void endQuery(long queryId) throws StorageEngineException { // close file stream of external sort files, and delete if (externalSortFileMap.get(queryId) != null) { diff --git a/server/src/main/java/org/apache/iotdb/db/query/dataset/UDTFAlignByTimeDataSet.java b/server/src/main/java/org/apache/iotdb/db/query/dataset/UDTFAlignByTimeDataSet.java index 4752434a7ff9..71a57089796b 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/dataset/UDTFAlignByTimeDataSet.java +++ b/server/src/main/java/org/apache/iotdb/db/query/dataset/UDTFAlignByTimeDataSet.java @@ -20,11 +20,11 @@ package org.apache.iotdb.db.query.dataset; import org.apache.iotdb.db.exception.query.QueryProcessException; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; import org.apache.iotdb.db.qp.physical.crud.UDTFPlan; import org.apache.iotdb.db.query.context.QueryContext; import org.apache.iotdb.db.query.reader.series.IReaderByTimestamp; import org.apache.iotdb.db.query.reader.series.ManagedSeriesReader; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; import org.apache.iotdb.db.tools.watermark.WatermarkEncoder; import org.apache.iotdb.db.utils.datastructure.TimeSelector; import org.apache.iotdb.service.rpc.thrift.TSQueryDataSet; @@ -246,7 +246,7 @@ public TSQueryDataSet fillBuffer(int fetchSize, WatermarkEncoder encoder) --rowOffset; } - rawQueryInputLayer.updateRowRecordListEvictionUpperBound(); + queryDataSetInputLayer.updateRowRecordListEvictionUpperBound(); } /* @@ -350,7 +350,7 @@ public RowRecord nextWithoutConstraint() throws IOException { throw new IOException(e.getMessage()); } - rawQueryInputLayer.updateRowRecordListEvictionUpperBound(); + queryDataSetInputLayer.updateRowRecordListEvictionUpperBound(); return rowRecord; } diff --git a/server/src/main/java/org/apache/iotdb/db/query/dataset/UDTFDataSet.java b/server/src/main/java/org/apache/iotdb/db/query/dataset/UDTFDataSet.java index 7c1dfa2af9d6..1f816f1a2a08 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/dataset/UDTFDataSet.java +++ b/server/src/main/java/org/apache/iotdb/db/query/dataset/UDTFDataSet.java @@ -22,13 +22,13 @@ import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.exception.query.QueryProcessException; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.dag.builder.DAGBuilder; +import org.apache.iotdb.db.mpp.transformation.dag.input.QueryDataSetInputLayer; import org.apache.iotdb.db.qp.physical.crud.UDTFPlan; import org.apache.iotdb.db.query.context.QueryContext; import org.apache.iotdb.db.query.reader.series.IReaderByTimestamp; import org.apache.iotdb.db.query.reader.series.ManagedSeriesReader; -import org.apache.iotdb.db.query.udf.core.layer.DAGBuilder; -import org.apache.iotdb.db.query.udf.core.layer.RawQueryInputLayer; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; import org.apache.iotdb.db.query.udf.service.UDFClassLoaderManager; import org.apache.iotdb.db.query.udf.service.UDFRegistrationService; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; @@ -50,7 +50,7 @@ public abstract class UDTFDataSet extends QueryDataSet { protected final long queryId; protected final UDTFPlan udtfPlan; - protected final RawQueryInputLayer rawQueryInputLayer; + protected final QueryDataSetInputLayer queryDataSetInputLayer; protected LayerPointReader[] transformers; @@ -68,8 +68,8 @@ protected UDTFDataSet( super(new ArrayList<>(deduplicatedPaths), deduplicatedDataTypes); queryId = queryContext.getQueryId(); this.udtfPlan = udtfPlan; - rawQueryInputLayer = - new RawQueryInputLayer( + queryDataSetInputLayer = + new QueryDataSetInputLayer( queryId, UDF_READER_MEMORY_BUDGET_IN_MB, deduplicatedPaths, @@ -94,8 +94,8 @@ protected UDTFDataSet( super(new ArrayList<>(deduplicatedPaths), deduplicatedDataTypes); queryId = queryContext.getQueryId(); this.udtfPlan = udtfPlan; - rawQueryInputLayer = - new RawQueryInputLayer( + queryDataSetInputLayer = + new QueryDataSetInputLayer( queryId, UDF_READER_MEMORY_BUDGET_IN_MB, udtfPlan, readersOfSelectedSeries); initTransformers(); @@ -106,7 +106,8 @@ public UDTFDataSet(QueryContext queryContext, UDTFPlan udtfPlan, IUDFInputDataSe throws QueryProcessException, IOException { queryId = queryContext.getQueryId(); this.udtfPlan = udtfPlan; - rawQueryInputLayer = new RawQueryInputLayer(queryId, UDF_READER_MEMORY_BUDGET_IN_MB, dataSet); + queryDataSetInputLayer = + new QueryDataSetInputLayer(queryId, UDF_READER_MEMORY_BUDGET_IN_MB, dataSet); initTransformers(); initDataSetFields(); } @@ -121,7 +122,7 @@ protected void initTransformers() throws QueryProcessException, IOException { new DAGBuilder( queryId, udtfPlan, - rawQueryInputLayer, + queryDataSetInputLayer, UDF_TRANSFORMER_MEMORY_BUDGET_IN_MB + UDF_COLLECTOR_MEMORY_BUDGET_IN_MB) .bindInputLayerColumnIndexWithExpression() .buildLayerMemoryAssigner() diff --git a/server/src/main/java/org/apache/iotdb/db/query/dataset/UDTFNonAlignDataSet.java b/server/src/main/java/org/apache/iotdb/db/query/dataset/UDTFNonAlignDataSet.java index 01af59cac8a2..53a2acf177f6 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/dataset/UDTFNonAlignDataSet.java +++ b/server/src/main/java/org/apache/iotdb/db/query/dataset/UDTFNonAlignDataSet.java @@ -20,11 +20,11 @@ package org.apache.iotdb.db.query.dataset; import org.apache.iotdb.db.exception.query.QueryProcessException; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; import org.apache.iotdb.db.qp.physical.crud.UDTFPlan; import org.apache.iotdb.db.query.context.QueryContext; import org.apache.iotdb.db.query.reader.series.IReaderByTimestamp; import org.apache.iotdb.db.query.reader.series.ManagedSeriesReader; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; import org.apache.iotdb.db.tools.watermark.WatermarkEncoder; import org.apache.iotdb.service.rpc.thrift.TSQueryNonAlignDataSet; import org.apache.iotdb.tsfile.exception.write.UnSupportedDataTypeException; @@ -115,7 +115,7 @@ public TSQueryNonAlignDataSet fillBuffer(int fetchSize, WatermarkEncoder encoder valueBufferList.add(timeValueByteBufferPair.right); } - rawQueryInputLayer.updateRowRecordListEvictionUpperBound(); + queryDataSetInputLayer.updateRowRecordListEvictionUpperBound(); tsQueryNonAlignDataSet.setTimeList(timeBufferList); tsQueryNonAlignDataSet.setValueList(valueBufferList); diff --git a/server/src/main/java/org/apache/iotdb/db/query/dataset/groupby/GroupByFillDataSet.java b/server/src/main/java/org/apache/iotdb/db/query/dataset/groupby/GroupByFillDataSet.java index 5b0c9a7607ae..d88fadc167c2 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/dataset/groupby/GroupByFillDataSet.java +++ b/server/src/main/java/org/apache/iotdb/db/query/dataset/groupby/GroupByFillDataSet.java @@ -22,13 +22,13 @@ import org.apache.iotdb.db.exception.StorageEngineException; import org.apache.iotdb.db.exception.query.QueryProcessException; import org.apache.iotdb.db.exception.query.UnSupportedFillTypeException; +import org.apache.iotdb.db.mpp.transformation.datastructure.tv.ElasticSerializableTVList; import org.apache.iotdb.db.qp.physical.crud.GroupByTimeFillPlan; import org.apache.iotdb.db.query.context.QueryContext; import org.apache.iotdb.db.query.executor.fill.IFill; import org.apache.iotdb.db.query.executor.fill.LinearFill; import org.apache.iotdb.db.query.executor.fill.PreviousFill; import org.apache.iotdb.db.query.executor.fill.ValueFill; -import org.apache.iotdb.db.query.udf.datastructure.tv.ElasticSerializableTVList; import org.apache.iotdb.db.utils.TypeInferenceUtils; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.read.TimeValuePair; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/service/UDFClassLoaderManager.java b/server/src/main/java/org/apache/iotdb/db/query/udf/service/UDFClassLoaderManager.java index 2af57647476f..99cf877d3e85 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/service/UDFClassLoaderManager.java +++ b/server/src/main/java/org/apache/iotdb/db/query/udf/service/UDFClassLoaderManager.java @@ -46,9 +46,8 @@ public class UDFClassLoaderManager implements IService { /** * activeClassLoader is used to load all classes under libRoot. libRoot may be updated before the * user executes CREATE FUNCTION or after the user executes DROP FUNCTION. Therefore, we need to - * continuously maintain the activeClassLoader so that the classes it loads are always up to date. + * continuously maintain the activeClassLoader so that the classes it loads are always up-to-date. */ - @SuppressWarnings("squid:S3077") private volatile UDFClassLoader activeClassLoader; UDFClassLoaderManager() { diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/service/UDFRegistrationInformation.java b/server/src/main/java/org/apache/iotdb/db/query/udf/service/UDFRegistrationInformation.java index c09092fada1d..729629c0b235 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/service/UDFRegistrationInformation.java +++ b/server/src/main/java/org/apache/iotdb/db/query/udf/service/UDFRegistrationInformation.java @@ -19,7 +19,7 @@ package org.apache.iotdb.db.query.udf.service; -import org.apache.iotdb.db.query.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.UDTF; import java.lang.reflect.InvocationTargetException; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/service/UDFRegistrationService.java b/server/src/main/java/org/apache/iotdb/db/query/udf/service/UDFRegistrationService.java index af451f5e383f..6c890da7f22e 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/service/UDFRegistrationService.java +++ b/server/src/main/java/org/apache/iotdb/db/query/udf/service/UDFRegistrationService.java @@ -23,14 +23,12 @@ import org.apache.iotdb.commons.file.SystemFileFactory; import org.apache.iotdb.commons.service.IService; import org.apache.iotdb.commons.service.ServiceType; +import org.apache.iotdb.commons.udf.api.UDF; +import org.apache.iotdb.commons.udf.builtin.BuiltinFunction; import org.apache.iotdb.commons.utils.TestOnly; import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.exception.UDFRegistrationException; -import org.apache.iotdb.db.exception.query.QueryProcessException; import org.apache.iotdb.db.qp.constant.SQLConstant; -import org.apache.iotdb.db.query.expression.multi.FunctionExpression; -import org.apache.iotdb.db.query.udf.api.UDF; -import org.apache.iotdb.db.query.udf.builtin.BuiltinFunction; import org.apache.iotdb.tsfile.fileSystem.FSFactoryProducer; import org.apache.commons.io.FileUtils; @@ -239,8 +237,8 @@ private void appendDeregistrationLog(String functionName) throws IOException { } } - public UDF reflect(FunctionExpression expression) throws QueryProcessException { - String functionName = expression.getFunctionName().toUpperCase(); + public UDF reflect(String functionName) { + functionName = functionName.toUpperCase(); UDFRegistrationInformation information = registrationInformation.get(functionName); if (information == null) { String errorMessage = @@ -248,7 +246,7 @@ public UDF reflect(FunctionExpression expression) throws QueryProcessException { "Failed to reflect UDF instance, because UDF %s has not been registered.", functionName); logger.warn(errorMessage); - throw new QueryProcessException(errorMessage); + throw new RuntimeException(errorMessage); } if (!information.isBuiltin()) { @@ -267,7 +265,7 @@ public UDF reflect(FunctionExpression expression) throws QueryProcessException { "Failed to reflect UDF %s(%s) instance, because %s", functionName, information.getClassName(), e); logger.warn(errorMessage); - throw new QueryProcessException(errorMessage); + throw new RuntimeException(errorMessage); } } diff --git a/server/src/main/java/org/apache/iotdb/db/service/DataNode.java b/server/src/main/java/org/apache/iotdb/db/service/DataNode.java index 8f51106bf953..d2333c674bce 100644 --- a/server/src/main/java/org/apache/iotdb/db/service/DataNode.java +++ b/server/src/main/java/org/apache/iotdb/db/service/DataNode.java @@ -49,7 +49,6 @@ import org.apache.iotdb.db.mpp.execution.schedule.DriverScheduler; import org.apache.iotdb.db.protocol.influxdb.meta.InfluxDBMetaManager; import org.apache.iotdb.db.protocol.rest.RestService; -import org.apache.iotdb.db.query.udf.service.TemporaryQueryDataFileService; import org.apache.iotdb.db.query.udf.service.UDFClassLoaderManager; import org.apache.iotdb.db.query.udf.service.UDFRegistrationService; import org.apache.iotdb.db.service.basic.ServiceProvider; diff --git a/server/src/main/java/org/apache/iotdb/db/service/IoTDB.java b/server/src/main/java/org/apache/iotdb/db/service/IoTDB.java index 54e2b3073a84..ced6730c575c 100644 --- a/server/src/main/java/org/apache/iotdb/db/service/IoTDB.java +++ b/server/src/main/java/org/apache/iotdb/db/service/IoTDB.java @@ -44,7 +44,6 @@ import org.apache.iotdb.db.mpp.execution.schedule.DriverScheduler; import org.apache.iotdb.db.protocol.influxdb.meta.InfluxDBMetaManager; import org.apache.iotdb.db.protocol.rest.RestService; -import org.apache.iotdb.db.query.udf.service.TemporaryQueryDataFileService; import org.apache.iotdb.db.query.udf.service.UDFClassLoaderManager; import org.apache.iotdb.db.query.udf.service.UDFRegistrationService; import org.apache.iotdb.db.rescon.PrimitiveArrayManager; diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/service/TemporaryQueryDataFileService.java b/server/src/main/java/org/apache/iotdb/db/service/TemporaryQueryDataFileService.java similarity index 96% rename from server/src/main/java/org/apache/iotdb/db/query/udf/service/TemporaryQueryDataFileService.java rename to server/src/main/java/org/apache/iotdb/db/service/TemporaryQueryDataFileService.java index 5c691bc8c436..4cd00dde1a5c 100644 --- a/server/src/main/java/org/apache/iotdb/db/query/udf/service/TemporaryQueryDataFileService.java +++ b/server/src/main/java/org/apache/iotdb/db/service/TemporaryQueryDataFileService.java @@ -17,14 +17,14 @@ * under the License. */ -package org.apache.iotdb.db.query.udf.service; +package org.apache.iotdb.db.service; import org.apache.iotdb.commons.exception.StartupException; import org.apache.iotdb.commons.file.SystemFileFactory; import org.apache.iotdb.commons.service.IService; import org.apache.iotdb.commons.service.ServiceType; import org.apache.iotdb.db.conf.IoTDBDescriptor; -import org.apache.iotdb.db.query.udf.datastructure.SerializableList.SerializationRecorder; +import org.apache.iotdb.db.mpp.transformation.datastructure.SerializableList.SerializationRecorder; import org.apache.commons.io.FileUtils; import org.slf4j.Logger; diff --git a/server/src/main/java/org/apache/iotdb/db/tools/watermark/WatermarkDetector.java b/server/src/main/java/org/apache/iotdb/db/tools/watermark/WatermarkDetector.java index 54b0a0bbf6cf..45f9e35a6f0b 100644 --- a/server/src/main/java/org/apache/iotdb/db/tools/watermark/WatermarkDetector.java +++ b/server/src/main/java/org/apache/iotdb/db/tools/watermark/WatermarkDetector.java @@ -68,7 +68,7 @@ public static void main(String[] args) throws IOException, LogicalOperatorExcept dataType); } - @SuppressWarnings("squid:S3776") // Suppress high Cognitive Complexity warning + // Suppress high Cognitive Complexity warning public static boolean isWatermarked( String filePath, String secretKey, @@ -171,12 +171,7 @@ private static long parseTimestamp(String str) throws LogicalOperatorException { try { timestamp = Long.parseLong(str); } catch (NumberFormatException e) { - try { - ZoneId zoneId = ZoneId.systemDefault(); - timestamp = DatetimeUtils.convertDatetimeStrToLong(str, zoneId); - } catch (LogicalOperatorException e1) { - throw new LogicalOperatorException("The format of timestamp is not unexpected."); - } + timestamp = DatetimeUtils.convertDatetimeStrToLong(str, ZoneId.systemDefault()); } return timestamp; } diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/QueryLogicalPlanUtil.java b/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/QueryLogicalPlanUtil.java index 411396ecd30c..fdcabbe1b42b 100644 --- a/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/QueryLogicalPlanUtil.java +++ b/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/QueryLogicalPlanUtil.java @@ -25,6 +25,13 @@ import org.apache.iotdb.db.metadata.path.MeasurementPath; import org.apache.iotdb.db.mpp.common.QueryId; import org.apache.iotdb.db.mpp.common.header.HeaderConstant; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.binary.GreaterThanExpression; +import org.apache.iotdb.db.mpp.plan.expression.binary.LogicAndExpression; +import org.apache.iotdb.db.mpp.plan.expression.leaf.ConstantOperand; +import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand; +import org.apache.iotdb.db.mpp.plan.expression.leaf.TimestampOperand; +import org.apache.iotdb.db.mpp.plan.expression.multi.FunctionExpression; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.process.AggregationNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.process.DeviceViewNode; @@ -44,13 +51,6 @@ import org.apache.iotdb.db.mpp.plan.statement.component.OrderBy; import org.apache.iotdb.db.qp.constant.SQLConstant; import org.apache.iotdb.db.query.aggregation.AggregationType; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.binary.GreaterThanExpression; -import org.apache.iotdb.db.query.expression.binary.LogicAndExpression; -import org.apache.iotdb.db.query.expression.leaf.ConstantOperand; -import org.apache.iotdb.db.query.expression.leaf.TimeSeriesOperand; -import org.apache.iotdb.db.query.expression.leaf.TimestampOperand; -import org.apache.iotdb.db.query.expression.multi.FunctionExpression; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.read.filter.TimeFilter; import org.apache.iotdb.tsfile.read.filter.basic.Filter; diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/process/AggregationNodeSerdeTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/process/AggregationNodeSerdeTest.java index 1ebddfd73661..1393453af9d0 100644 --- a/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/process/AggregationNodeSerdeTest.java +++ b/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/process/AggregationNodeSerdeTest.java @@ -24,6 +24,7 @@ import org.apache.iotdb.commons.exception.IllegalPathException; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.metadata.path.MeasurementPath; +import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand; import org.apache.iotdb.db.mpp.plan.plan.node.PlanNodeDeserializeHelper; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId; import org.apache.iotdb.db.mpp.plan.planner.plan.node.process.AggregationNode; @@ -33,7 +34,6 @@ import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.GroupByTimeParameter; import org.apache.iotdb.db.mpp.plan.statement.component.OrderBy; import org.apache.iotdb.db.query.aggregation.AggregationType; -import org.apache.iotdb.db.query.expression.leaf.TimeSeriesOperand; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.read.filter.operator.In; diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/process/FilterNodeSerdeTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/process/FilterNodeSerdeTest.java index 35346f29d86f..43310e8f47c0 100644 --- a/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/process/FilterNodeSerdeTest.java +++ b/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/process/FilterNodeSerdeTest.java @@ -20,15 +20,15 @@ import org.apache.iotdb.commons.exception.IllegalPathException; import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.binary.GreaterThanExpression; +import org.apache.iotdb.db.mpp.plan.expression.leaf.ConstantOperand; +import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand; import org.apache.iotdb.db.mpp.plan.plan.node.PlanNodeDeserializeHelper; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId; import org.apache.iotdb.db.mpp.plan.planner.plan.node.process.FilterNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.process.TimeJoinNode; import org.apache.iotdb.db.mpp.plan.statement.component.OrderBy; -import org.apache.iotdb.db.query.expression.Expression; -import org.apache.iotdb.db.query.expression.binary.GreaterThanExpression; -import org.apache.iotdb.db.query.expression.leaf.ConstantOperand; -import org.apache.iotdb.db.query.expression.leaf.TimeSeriesOperand; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.junit.Test; diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/process/FilterNullNodeSerdeTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/process/FilterNullNodeSerdeTest.java index 2aeac4187143..360b644d8d5a 100644 --- a/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/process/FilterNullNodeSerdeTest.java +++ b/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/process/FilterNullNodeSerdeTest.java @@ -20,13 +20,13 @@ import org.apache.iotdb.commons.exception.IllegalPathException; import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand; import org.apache.iotdb.db.mpp.plan.plan.node.PlanNodeDeserializeHelper; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId; import org.apache.iotdb.db.mpp.plan.planner.plan.node.process.FilterNullNode; import org.apache.iotdb.db.mpp.plan.planner.plan.node.process.TimeJoinNode; import org.apache.iotdb.db.mpp.plan.statement.component.FilterNullPolicy; import org.apache.iotdb.db.mpp.plan.statement.component.OrderBy; -import org.apache.iotdb.db.query.expression.leaf.TimeSeriesOperand; import org.junit.Test; diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/process/GroupByLevelNodeSerdeTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/process/GroupByLevelNodeSerdeTest.java index 7e94ffff86ea..53a4b72b68d7 100644 --- a/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/process/GroupByLevelNodeSerdeTest.java +++ b/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/process/GroupByLevelNodeSerdeTest.java @@ -24,6 +24,7 @@ import org.apache.iotdb.commons.exception.IllegalPathException; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.metadata.path.MeasurementPath; +import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand; import org.apache.iotdb.db.mpp.plan.plan.node.PlanNodeDeserializeHelper; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId; import org.apache.iotdb.db.mpp.plan.planner.plan.node.process.GroupByLevelNode; @@ -33,7 +34,6 @@ import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.GroupByTimeParameter; import org.apache.iotdb.db.mpp.plan.statement.component.OrderBy; import org.apache.iotdb.db.query.aggregation.AggregationType; -import org.apache.iotdb.db.query.expression.leaf.TimeSeriesOperand; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.junit.Test; diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/source/SeriesAggregationScanNodeSerdeTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/source/SeriesAggregationScanNodeSerdeTest.java index 072cf1a0a4d0..9b07836baf2c 100644 --- a/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/source/SeriesAggregationScanNodeSerdeTest.java +++ b/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/node/source/SeriesAggregationScanNodeSerdeTest.java @@ -25,6 +25,7 @@ import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.exception.query.QueryProcessException; import org.apache.iotdb.db.metadata.path.MeasurementPath; +import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand; import org.apache.iotdb.db.mpp.plan.plan.node.PlanNodeDeserializeHelper; import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId; import org.apache.iotdb.db.mpp.plan.planner.plan.node.source.SeriesAggregationScanNode; @@ -33,7 +34,6 @@ import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.GroupByTimeParameter; import org.apache.iotdb.db.mpp.plan.statement.component.OrderBy; import org.apache.iotdb.db.query.aggregation.AggregationType; -import org.apache.iotdb.db.query.expression.leaf.TimeSeriesOperand; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.read.filter.operator.In; diff --git a/server/src/test/java/org/apache/iotdb/db/protocol/influxdb/sql/InfluxDBLogicalGeneratorTest.java b/server/src/test/java/org/apache/iotdb/db/protocol/influxdb/sql/InfluxDBLogicalGeneratorTest.java index 31c662cdefc1..11ab3a6a1f33 100644 --- a/server/src/test/java/org/apache/iotdb/db/protocol/influxdb/sql/InfluxDBLogicalGeneratorTest.java +++ b/server/src/test/java/org/apache/iotdb/db/protocol/influxdb/sql/InfluxDBLogicalGeneratorTest.java @@ -18,12 +18,12 @@ */ package org.apache.iotdb.db.protocol.influxdb.sql; +import org.apache.iotdb.db.mpp.plan.expression.ResultColumn; +import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand; import org.apache.iotdb.db.protocol.influxdb.operator.InfluxQueryOperator; import org.apache.iotdb.db.qp.logical.crud.BasicFunctionOperator; import org.apache.iotdb.db.qp.logical.crud.FilterOperator; import org.apache.iotdb.db.qp.logical.crud.WhereComponent; -import org.apache.iotdb.db.query.expression.ResultColumn; -import org.apache.iotdb.db.query.expression.leaf.TimeSeriesOperand; import org.junit.Test; diff --git a/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/ElasticSerializableRowRecordListTest.java b/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/ElasticSerializableRowRecordListTest.java index 6ed4fb2b3190..4a10c084635a 100644 --- a/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/ElasticSerializableRowRecordListTest.java +++ b/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/ElasticSerializableRowRecordListTest.java @@ -20,7 +20,8 @@ package org.apache.iotdb.db.query.udf.datastructure; import org.apache.iotdb.db.exception.query.QueryProcessException; -import org.apache.iotdb.db.query.udf.datastructure.row.ElasticSerializableRowRecordList; +import org.apache.iotdb.db.mpp.transformation.datastructure.SerializableList; +import org.apache.iotdb.db.mpp.transformation.datastructure.row.ElasticSerializableRowRecordList; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.utils.Binary; diff --git a/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/ElasticSerializableTVListTest.java b/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/ElasticSerializableTVListTest.java index 865c27026664..18a12b8d7377 100644 --- a/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/ElasticSerializableTVListTest.java +++ b/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/ElasticSerializableTVListTest.java @@ -20,8 +20,9 @@ package org.apache.iotdb.db.query.udf.datastructure; import org.apache.iotdb.db.exception.query.QueryProcessException; -import org.apache.iotdb.db.query.udf.core.reader.LayerPointReader; -import org.apache.iotdb.db.query.udf.datastructure.tv.ElasticSerializableTVList; +import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader; +import org.apache.iotdb.db.mpp.transformation.datastructure.SerializableList; +import org.apache.iotdb.db.mpp.transformation.datastructure.tv.ElasticSerializableTVList; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.utils.Binary; @@ -92,13 +93,9 @@ private void testESTVList(TSDataType dataType) { } private void initESTVList(TSDataType dataType) { - try { - tvList = - ElasticSerializableTVList.newElasticSerializableTVList( - dataType, QUERY_ID, MEMORY_USAGE_LIMIT_IN_MB, CACHE_SIZE); - } catch (QueryProcessException e) { - fail(e.toString()); - } + tvList = + ElasticSerializableTVList.newElasticSerializableTVList( + dataType, QUERY_ID, MEMORY_USAGE_LIMIT_IN_MB, CACHE_SIZE); assertEquals(0, tvList.size()); } @@ -160,7 +157,7 @@ private void testPut(TSDataType dataType) { } break; } - } catch (IOException | QueryProcessException e) { + } catch (IOException e) { fail(e.toString()); } assertEquals(ITERATION_TIMES, tvList.size()); diff --git a/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/LRUCache.java b/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/LRUCache.java index 5761b09c90bf..9447b224190b 100644 --- a/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/LRUCache.java +++ b/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/LRUCache.java @@ -19,6 +19,8 @@ package org.apache.iotdb.db.query.udf.datastructure; +import org.apache.iotdb.db.mpp.transformation.datastructure.Cache; + import java.util.Arrays; public class LRUCache extends Cache { diff --git a/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/SerializableBinaryTVListTest.java b/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/SerializableBinaryTVListTest.java index 1338f27bfb01..726a629da644 100644 --- a/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/SerializableBinaryTVListTest.java +++ b/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/SerializableBinaryTVListTest.java @@ -19,8 +19,8 @@ package org.apache.iotdb.db.query.udf.datastructure; -import org.apache.iotdb.db.query.udf.datastructure.tv.SerializableBinaryTVList; -import org.apache.iotdb.db.query.udf.datastructure.tv.SerializableTVList; +import org.apache.iotdb.db.mpp.transformation.datastructure.tv.SerializableBinaryTVList; +import org.apache.iotdb.db.mpp.transformation.datastructure.tv.SerializableTVList; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.utils.Binary; diff --git a/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/SerializableBooleanTVListTest.java b/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/SerializableBooleanTVListTest.java index 6203fe358195..736f605d12b6 100644 --- a/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/SerializableBooleanTVListTest.java +++ b/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/SerializableBooleanTVListTest.java @@ -19,8 +19,8 @@ package org.apache.iotdb.db.query.udf.datastructure; -import org.apache.iotdb.db.query.udf.datastructure.tv.SerializableBooleanTVList; -import org.apache.iotdb.db.query.udf.datastructure.tv.SerializableTVList; +import org.apache.iotdb.db.mpp.transformation.datastructure.tv.SerializableBooleanTVList; +import org.apache.iotdb.db.mpp.transformation.datastructure.tv.SerializableTVList; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.junit.After; diff --git a/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/SerializableDoubleTVListTest.java b/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/SerializableDoubleTVListTest.java index 6f1914bc81ad..9c0a484abe2b 100644 --- a/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/SerializableDoubleTVListTest.java +++ b/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/SerializableDoubleTVListTest.java @@ -19,8 +19,8 @@ package org.apache.iotdb.db.query.udf.datastructure; -import org.apache.iotdb.db.query.udf.datastructure.tv.SerializableDoubleTVList; -import org.apache.iotdb.db.query.udf.datastructure.tv.SerializableTVList; +import org.apache.iotdb.db.mpp.transformation.datastructure.tv.SerializableDoubleTVList; +import org.apache.iotdb.db.mpp.transformation.datastructure.tv.SerializableTVList; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.junit.After; diff --git a/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/SerializableFloatTVListTest.java b/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/SerializableFloatTVListTest.java index 346ccbd3af83..ce43b3eb60bc 100644 --- a/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/SerializableFloatTVListTest.java +++ b/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/SerializableFloatTVListTest.java @@ -19,8 +19,8 @@ package org.apache.iotdb.db.query.udf.datastructure; -import org.apache.iotdb.db.query.udf.datastructure.tv.SerializableFloatTVList; -import org.apache.iotdb.db.query.udf.datastructure.tv.SerializableTVList; +import org.apache.iotdb.db.mpp.transformation.datastructure.tv.SerializableFloatTVList; +import org.apache.iotdb.db.mpp.transformation.datastructure.tv.SerializableTVList; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.junit.After; diff --git a/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/SerializableIntTVListTest.java b/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/SerializableIntTVListTest.java index 33d9b4c1261c..0571979795a3 100644 --- a/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/SerializableIntTVListTest.java +++ b/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/SerializableIntTVListTest.java @@ -19,8 +19,8 @@ package org.apache.iotdb.db.query.udf.datastructure; -import org.apache.iotdb.db.query.udf.datastructure.tv.SerializableIntTVList; -import org.apache.iotdb.db.query.udf.datastructure.tv.SerializableTVList; +import org.apache.iotdb.db.mpp.transformation.datastructure.tv.SerializableIntTVList; +import org.apache.iotdb.db.mpp.transformation.datastructure.tv.SerializableTVList; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.junit.After; diff --git a/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/SerializableListTest.java b/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/SerializableListTest.java index 81bb35aaddd1..dbbd2c2bfaf8 100644 --- a/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/SerializableListTest.java +++ b/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/SerializableListTest.java @@ -19,7 +19,7 @@ package org.apache.iotdb.db.query.udf.datastructure; -import org.apache.iotdb.db.query.udf.service.TemporaryQueryDataFileService; +import org.apache.iotdb.db.service.TemporaryQueryDataFileService; public abstract class SerializableListTest { diff --git a/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/SerializableLongTVListTest.java b/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/SerializableLongTVListTest.java index dfc181fb220d..ace0af972036 100644 --- a/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/SerializableLongTVListTest.java +++ b/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/SerializableLongTVListTest.java @@ -19,8 +19,8 @@ package org.apache.iotdb.db.query.udf.datastructure; -import org.apache.iotdb.db.query.udf.datastructure.tv.SerializableLongTVList; -import org.apache.iotdb.db.query.udf.datastructure.tv.SerializableTVList; +import org.apache.iotdb.db.mpp.transformation.datastructure.tv.SerializableLongTVList; +import org.apache.iotdb.db.mpp.transformation.datastructure.tv.SerializableTVList; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.junit.After; diff --git a/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/SerializableRowRecordListTest.java b/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/SerializableRowRecordListTest.java index 29084b4ab5de..af19c6cc1f85 100644 --- a/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/SerializableRowRecordListTest.java +++ b/server/src/test/java/org/apache/iotdb/db/query/udf/datastructure/SerializableRowRecordListTest.java @@ -19,7 +19,7 @@ package org.apache.iotdb.db.query.udf.datastructure; -import org.apache.iotdb.db.query.udf.datastructure.row.SerializableRowRecordList; +import org.apache.iotdb.db.mpp.transformation.datastructure.row.SerializableRowRecordList; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.read.common.Field; import org.apache.iotdb.tsfile.read.common.RowRecord; diff --git a/server/src/test/java/org/apache/iotdb/db/query/udf/example/Adder.java b/server/src/test/java/org/apache/iotdb/db/query/udf/example/Adder.java index 2b1fc1f13dc1..8b9ea955f48f 100644 --- a/server/src/test/java/org/apache/iotdb/db/query/udf/example/Adder.java +++ b/server/src/test/java/org/apache/iotdb/db/query/udf/example/Adder.java @@ -19,13 +19,13 @@ package org.apache.iotdb.db.query.udf.example; -import org.apache.iotdb.db.query.udf.api.UDTF; -import org.apache.iotdb.db.query.udf.api.access.Row; -import org.apache.iotdb.db.query.udf.api.collector.PointCollector; -import org.apache.iotdb.db.query.udf.api.customizer.config.UDTFConfigurations; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameterValidator; -import org.apache.iotdb.db.query.udf.api.customizer.parameter.UDFParameters; -import org.apache.iotdb.db.query.udf.api.customizer.strategy.RowByRowAccessStrategy; +import org.apache.iotdb.commons.udf.api.UDTF; +import org.apache.iotdb.commons.udf.api.access.Row; +import org.apache.iotdb.commons.udf.api.collector.PointCollector; +import org.apache.iotdb.commons.udf.api.customizer.config.UDTFConfigurations; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameterValidator; +import org.apache.iotdb.commons.udf.api.customizer.parameter.UDFParameters; +import org.apache.iotdb.commons.udf.api.customizer.strategy.RowByRowAccessStrategy; import org.apache.iotdb.tsfile.exception.write.UnSupportedDataTypeException; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; From 8f3c60537b7ee16f0e1670ebfb4fc2b0e56eb167 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 22 May 2022 01:57:41 +0800 Subject: [PATCH 066/436] Bump gson from 2.8.5 to 2.8.9 in /library-udf (#5979) Bumps [gson](https://github.com/google/gson) from 2.8.5 to 2.8.9. - [Release notes](https://github.com/google/gson/releases) - [Changelog](https://github.com/google/gson/blob/master/CHANGELOG.md) - [Commits](https://github.com/google/gson/compare/gson-parent-2.8.5...gson-parent-2.8.9) --- updated-dependencies: - dependency-name: com.google.code.gson:gson dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- library-udf/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library-udf/pom.xml b/library-udf/pom.xml index 4192290ad9c8..34e1395dc4ff 100644 --- a/library-udf/pom.xml +++ b/library-udf/pom.xml @@ -96,7 +96,7 @@ com.google.code.gson gson - 2.8.5 + 2.8.9 org.junit.jupiter From 9a1bc7ccaf4b36cd89813182ae121f7afbbf2b9c Mon Sep 17 00:00:00 2001 From: Jack Tsai Date: Sun, 22 May 2022 10:35:51 +0800 Subject: [PATCH 067/436] [IOTDB-3128] Homepage does not work well on mobile (#5975) Co-authored-by: tsunghanjacktsai --- .../theme/global-components/IoTDB.vue | 72 ++++++++++--------- 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/site/src/main/.vuepress/theme/global-components/IoTDB.vue b/site/src/main/.vuepress/theme/global-components/IoTDB.vue index 2e3312135688..2eeb963ad845 100644 --- a/site/src/main/.vuepress/theme/global-components/IoTDB.vue +++ b/site/src/main/.vuepress/theme/global-components/IoTDB.vue @@ -19,41 +19,32 @@