This is an automated email from the ASF dual-hosted git repository.
lide pushed a commit to branch branch-2.0
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-2.0 by this push:
new 82e33fda952 [feature](cmd) add UNSET_VARIABLE statement to set back
variables (#34949)
82e33fda952 is described below
commit 82e33fda9525d0a48cacd0e9f77522fa3f5f2db2
Author: Yulei-Yang <[email protected]>
AuthorDate: Thu May 16 19:25:47 2024 +0800
[feature](cmd) add UNSET_VARIABLE statement to set back variables (#34949)
---
.../antlr4/org/apache/doris/nereids/DorisLexer.g4 | 2 +
.../antlr4/org/apache/doris/nereids/DorisParser.g4 | 2 +
fe/fe-core/src/main/cup/sql_parser.cup | 22 +++-
.../apache/doris/analysis/UnsetVariableStmt.java | 97 ++++++++++++++++
.../java/org/apache/doris/qe/StmtExecutor.java | 42 +++++++
.../main/java/org/apache/doris/qe/VariableMgr.java | 23 ++++
fe/fe-core/src/main/jflex/sql_scanner.flex | 2 +
.../data/variable_p0/set_and_unset_variable.out | 127 +++++++++++++++++++++
.../variable_p0/set_and_unset_variable.groovy | 72 ++++++++++++
9 files changed, 388 insertions(+), 1 deletion(-)
diff --git a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisLexer.g4
b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisLexer.g4
index e82538e1e20..188578b7d49 100644
--- a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisLexer.g4
+++ b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisLexer.g4
@@ -526,6 +526,7 @@ UNINSTALL: 'UNINSTALL';
UNION: 'UNION';
UNIQUE: 'UNIQUE';
UNLOCK: 'UNLOCK';
+UNSET: 'UNSET';
UNSIGNED: 'UNSIGNED';
UPDATE: 'UPDATE';
USE: 'USE';
@@ -534,6 +535,7 @@ USING: 'USING';
VALUE: 'VALUE';
VALUES: 'VALUES';
VARCHAR: 'VARCHAR';
+VARIABLE: 'VARIABLE';
VARIABLES: 'VARIABLES';
VERBOSE: 'VERBOSE';
VERSION: 'VERSION';
diff --git a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
index c830a2f0d7e..d3e184a1c76 100644
--- a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
+++ b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
@@ -833,9 +833,11 @@ nonReserved
| TYPES
| UNCOMMITTED
| UNLOCK
+ | UNSET
| USER
| VALUE
| VARCHAR
+ | VARIABLE
| VARIABLES
| VERBOSE
| VERSION
diff --git a/fe/fe-core/src/main/cup/sql_parser.cup
b/fe/fe-core/src/main/cup/sql_parser.cup
index c6eb2ec7eab..a27d896c3ed 100644
--- a/fe/fe-core/src/main/cup/sql_parser.cup
+++ b/fe/fe-core/src/main/cup/sql_parser.cup
@@ -623,6 +623,7 @@ terminal String
KW_UNION,
KW_UNIQUE,
KW_UNLOCK,
+ KW_UNSET,
KW_UNSIGNED,
KW_UPDATE,
KW_USE,
@@ -631,6 +632,7 @@ terminal String
KW_VALUE,
KW_VALUES,
KW_VARCHAR,
+ KW_VARIABLE,
KW_VARIABLES,
KW_VERBOSE,
KW_VERSION,
@@ -675,7 +677,7 @@ nonterminal List<StatementBase> stmts;
nonterminal StatementBase stmt, show_stmt, show_param, help_stmt, load_stmt,
create_routine_load_stmt, pause_routine_load_stmt,
resume_routine_load_stmt, stop_routine_load_stmt,
show_routine_load_stmt, show_routine_load_task_stmt,
show_create_routine_load_stmt, show_create_load_stmt,
show_create_reporitory_stmt,
- describe_stmt, alter_stmt,
+ describe_stmt, alter_stmt, unset_var_stmt,
use_stmt, kill_stmt, drop_stmt, recover_stmt, grant_stmt, revoke_stmt,
create_stmt, set_stmt, sync_stmt, cancel_stmt, cancel_param, delete_stmt,
switch_stmt, transaction_stmt, unsupported_stmt, export_stmt, admin_stmt,
truncate_stmt,
import_columns_stmt, import_delete_on_stmt, import_sequence_stmt,
import_where_stmt, install_plugin_stmt, uninstall_plugin_stmt,
@@ -1105,6 +1107,8 @@ stmt ::=
{: RESULT = use; :}
| set_stmt:set
{: RESULT = set; :}
+ | unset_var_stmt:stmt
+ {: RESULT = stmt; :}
| kill_stmt:kill
{: RESULT = kill; :}
| kill_analysis_job_stmt: k
@@ -5006,6 +5010,18 @@ set_stmt ::=
:}
;
+// Unset variable statement
+unset_var_stmt ::=
+ KW_UNSET opt_var_type:type KW_VARIABLE variable_name:variable
+ {:
+ RESULT = new UnsetVariableStmt(type, variable);
+ :}
+ | KW_UNSET opt_var_type:type KW_VARIABLE KW_ALL
+ {:
+ RESULT = new UnsetVariableStmt(type, true);
+ :}
+ ;
+
user_property_list ::=
user_property:property
{:
@@ -7757,6 +7773,8 @@ keyword ::=
{: RESULT = id; :}
| KW_USER:id
{: RESULT = id; :}
+ | KW_VARIABLE:id
+ {: RESULT = id; :}
| KW_VARIABLES:id
{: RESULT = id; :}
| KW_VALUE:id
@@ -7793,6 +7811,8 @@ keyword ::=
{: RESULT = id; :}
| KW_FREE:id
{: RESULT = id; :}
+ | KW_UNSET:id
+ {: RESULT = id; :}
| KW_TASK:id
{: RESULT = id; :}
| KW_ROUTINE:id
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/analysis/UnsetVariableStmt.java
b/fe/fe-core/src/main/java/org/apache/doris/analysis/UnsetVariableStmt.java
new file mode 100644
index 00000000000..1f456eb5b7a
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/UnsetVariableStmt.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.doris.analysis;
+
+import org.apache.doris.common.AnalysisException;
+import org.apache.doris.common.UserException;
+
+import com.amazonaws.util.StringUtils;
+
+// Unset variables statement
+public class UnsetVariableStmt extends StatementBase {
+ private SetType setType;
+
+ // variables to restore
+ private String variable = null;
+
+ private boolean applyToAll = false;
+
+ public UnsetVariableStmt(SetType setType, String varName) {
+ this.setType = setType;
+ this.variable = varName;
+ }
+
+ public UnsetVariableStmt(SetType setType, boolean applyToAll) {
+ this.setType = setType;
+ this.applyToAll = applyToAll;
+ }
+
+ public SetType getSetType() {
+ return setType;
+ }
+
+ public String getVariable() {
+ return variable;
+ }
+
+ public boolean isApplyToAll() {
+ return applyToAll;
+ }
+
+ // change type global to session avoid to write in non-master node.
+ public void modifySetVarsForExecute() {
+ if (setType == SetType.GLOBAL) {
+ setType = SetType.SESSION;
+ }
+ }
+
+ @Override
+ public void analyze(Analyzer analyzer) throws UserException {
+ if (StringUtils.isNullOrEmpty(variable) && !applyToAll) {
+ throw new AnalysisException("You should specific the unset
variable.");
+ }
+ }
+
+ @Override
+ public String toSql() {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append("UNSET ");
+ sb.append(setType).append(" VARIABLE ");
+ if (!StringUtils.isNullOrEmpty(variable)) {
+ sb.append(variable).append(" ");
+ } else if (applyToAll) {
+ sb.append("ALL");
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public String toString() {
+ return toSql();
+ }
+
+ @Override
+ public RedirectStatus getRedirectStatus() {
+ if (setType == SetType.GLOBAL) {
+ return RedirectStatus.FORWARD_WITH_SYNC;
+ }
+
+ return RedirectStatus.NO_FORWARD;
+ }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java
b/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java
index 1f0e4d43d20..14b76daa8ed 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java
@@ -56,7 +56,9 @@ import org.apache.doris.analysis.ReplaceTableClause;
import org.apache.doris.analysis.SelectStmt;
import org.apache.doris.analysis.SetOperationStmt;
import org.apache.doris.analysis.SetStmt;
+import org.apache.doris.analysis.SetType;
import org.apache.doris.analysis.SetVar;
+import org.apache.doris.analysis.SetVar.SetVarType;
import org.apache.doris.analysis.ShowStmt;
import org.apache.doris.analysis.SqlParser;
import org.apache.doris.analysis.SqlScanner;
@@ -73,6 +75,7 @@ import org.apache.doris.analysis.TransactionRollbackStmt;
import org.apache.doris.analysis.TransactionStmt;
import org.apache.doris.analysis.UnifiedLoadStmt;
import org.apache.doris.analysis.UnlockTablesStmt;
+import org.apache.doris.analysis.UnsetVariableStmt;
import org.apache.doris.analysis.UnsupportedStmt;
import org.apache.doris.analysis.UpdateStmt;
import org.apache.doris.analysis.UseStmt;
@@ -773,6 +776,8 @@ public class StmtExecutor {
handleQueryWithRetry(queryId);
} else if (parsedStmt instanceof SetStmt) {
handleSetStmt();
+ } else if (parsedStmt instanceof UnsetVariableStmt) {
+ handleUnsetVariableStmt();
} else if (parsedStmt instanceof SwitchStmt) {
handleSwitchStmt();
} else if (parsedStmt instanceof UseStmt) {
@@ -918,6 +923,19 @@ public class StmtExecutor {
for (SetVar var : setStmt.getSetVars()) {
VariableMgr.setVarForNonMasterFE(context.getSessionVariable(),
var);
}
+ } else if (parsedStmt instanceof UnsetVariableStmt) {
+ UnsetVariableStmt unsetStmt = (UnsetVariableStmt) parsedStmt;
+ if (unsetStmt.isApplyToAll()) {
+
VariableMgr.setAllVarsToDefaultValue(context.getSessionVariable(),
SetType.SESSION);
+ } else {
+ String defaultValue =
VariableMgr.getDefaultValue(unsetStmt.getVariable());
+ if (defaultValue == null) {
+
ErrorReport.reportDdlException(ErrorCode.ERR_UNKNOWN_SYSTEM_VARIABLE,
unsetStmt.getVariable());
+ }
+ SetVar var = new SetVar(SetType.SESSION,
unsetStmt.getVariable(),
+ new StringLiteral(defaultValue),
SetVarType.SET_SESSION_VAR);
+ VariableMgr.setVar(context.getSessionVariable(), var);
+ }
}
}
@@ -1302,6 +1320,30 @@ public class StmtExecutor {
context.getState().setOk();
}
+ // Process unset variable statement.
+ private void handleUnsetVariableStmt() {
+ try {
+ UnsetVariableStmt unsetStmt = (UnsetVariableStmt) parsedStmt;
+ if (unsetStmt.isApplyToAll()) {
+
VariableMgr.setAllVarsToDefaultValue(context.getSessionVariable(),
unsetStmt.getSetType());
+ } else {
+ String defaultValue =
VariableMgr.getDefaultValue(unsetStmt.getVariable());
+ if (defaultValue == null) {
+
ErrorReport.reportDdlException(ErrorCode.ERR_UNKNOWN_SYSTEM_VARIABLE,
unsetStmt.getVariable());
+ }
+ SetVar var = new SetVar(unsetStmt.getSetType(),
unsetStmt.getVariable(),
+ new StringLiteral(defaultValue),
SetVarType.SET_SESSION_VAR);
+ VariableMgr.setVar(context.getSessionVariable(), var);
+ }
+ } catch (DdlException e) {
+ LOG.warn("", e);
+ // Return error message to client.
+ context.getState().setError(ErrorCode.ERR_LOCAL_VARIABLE,
e.getMessage());
+ return;
+ }
+ context.getState().setOk();
+ }
+
// send values from cache.
// return true if the meta fields has been sent, otherwise, return false.
// the meta fields must be sent right before the first batch of data(or
eos flag).
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/VariableMgr.java
b/fe/fe-core/src/main/java/org/apache/doris/qe/VariableMgr.java
index f2637f12acd..85224c6aef7 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/VariableMgr.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/VariableMgr.java
@@ -20,6 +20,8 @@ package org.apache.doris.qe;
import org.apache.doris.analysis.LiteralExpr;
import org.apache.doris.analysis.SetType;
import org.apache.doris.analysis.SetVar;
+import org.apache.doris.analysis.SetVar.SetVarType;
+import org.apache.doris.analysis.StringLiteral;
import org.apache.doris.analysis.VariableExpr;
import org.apache.doris.catalog.Env;
import org.apache.doris.catalog.Type;
@@ -651,6 +653,27 @@ public class VariableMgr {
return ImmutableMap.copyOf(result);
}
+ public static void setAllVarsToDefaultValue(SessionVariable
sessionVariable, SetType setType)
+ throws DdlException {
+ for (Map.Entry<String, VarContext> entry :
ctxByDisplayVarName.entrySet()) {
+ VarContext varCtx = entry.getValue();
+ SetVar setVar = new SetVar(setType, entry.getKey(),
+ new StringLiteral(varCtx.defaultValue),
SetVarType.SET_SESSION_VAR);
+ try {
+ checkUpdate(setVar, varCtx.getFlag());
+ } catch (DdlException e) {
+ LOG.debug("no need to set var for non master fe: {}",
setVar.getVariable(), e);
+ continue;
+ }
+ setVarInternal(sessionVariable, setVar, varCtx);
+ }
+ }
+
+ public static String getDefaultValue(String key) {
+ VarContext varContext = ctxByDisplayVarName.get(key);
+ return varContext == null ? null : varContext.defaultValue;
+ }
+
// Dump all fields. Used for `show variables`
public static List<List<String>> dump(SetType type, SessionVariable
sessionVar, PatternMatcher matcher) {
List<List<String>> rows = Lists.newArrayList();
diff --git a/fe/fe-core/src/main/jflex/sql_scanner.flex
b/fe/fe-core/src/main/jflex/sql_scanner.flex
index 2922eac328f..af731095076 100644
--- a/fe/fe-core/src/main/jflex/sql_scanner.flex
+++ b/fe/fe-core/src/main/jflex/sql_scanner.flex
@@ -506,6 +506,8 @@ import org.apache.doris.qe.SqlModeHelper;
keywordMap.put("ignore", new Integer(SqlParserSymbols.KW_IGNORE));
keywordMap.put("expired", new Integer(SqlParserSymbols.KW_EXPIRED));
keywordMap.put("convert_light_schema_change_process", new
Integer(SqlParserSymbols.KW_CONVERT_LSC));
+ keywordMap.put("unset", new Integer(SqlParserSymbols.KW_UNSET));
+ keywordMap.put("variable", new Integer(SqlParserSymbols.KW_VARIABLE));
}
// map from token id to token description
diff --git a/regression-test/data/variable_p0/set_and_unset_variable.out
b/regression-test/data/variable_p0/set_and_unset_variable.out
new file mode 100644
index 00000000000..5436bc1322f
--- /dev/null
+++ b/regression-test/data/variable_p0/set_and_unset_variable.out
@@ -0,0 +1,127 @@
+-- This file is automatically generated. You should know what you did if you
want to edit this
+-- !cmd --
+0
+
+-- !cmd --
+wait_timeout 1000 28800 1
+
+-- !cmd --
+0
+
+-- !cmd --
+wait_timeout 28800 28800 0
+
+-- !cmd --
+0
+
+-- !cmd --
+runtime_filter_type BLOOM_FILTER IN_OR_BLOOM_FILTER 1
+
+-- !cmd --
+runtime_filter_type IN_OR_BLOOM_FILTER IN_OR_BLOOM_FILTER 0
+
+-- !cmd --
+0
+
+-- !cmd --
+runtime_filter_type IN_OR_BLOOM_FILTER IN_OR_BLOOM_FILTER 0
+
+-- !cmd --
+runtime_filter_type IN_OR_BLOOM_FILTER IN_OR_BLOOM_FILTER 0
+
+-- !cmd --
+0
+
+-- !cmd --
+runtime_filter_type BLOOM_FILTER IN_OR_BLOOM_FILTER 1
+
+-- !cmd --
+runtime_filter_type BLOOM_FILTER IN_OR_BLOOM_FILTER 1
+
+-- !cmd --
+0
+
+-- !cmd --
+runtime_filter_type IN_OR_BLOOM_FILTER IN_OR_BLOOM_FILTER 0
+
+-- !cmd --
+runtime_filter_type IN_OR_BLOOM_FILTER IN_OR_BLOOM_FILTER 0
+
+-- !cmd --
+0
+
+-- !cmd --
+experimental_enable_agg_state true false 1
+
+-- !cmd --
+experimental_enable_agg_state false false 0
+
+-- !cmd --
+0
+
+-- !cmd --
+experimental_enable_agg_state false false 0
+
+-- !cmd --
+experimental_enable_agg_state false false 0
+
+-- !cmd --
+0
+
+-- !cmd --
+experimental_enable_agg_state true false 1
+
+-- !cmd --
+experimental_enable_agg_state true false 1
+
+-- !cmd --
+0
+
+-- !cmd --
+experimental_enable_agg_state false false 0
+
+-- !cmd --
+experimental_enable_agg_state false false 0
+
+-- !cmd --
+0
+
+-- !cmd --
+0
+
+-- !cmd --
+0
+
+-- !cmd --
+0
+
+-- !cmd --
+runtime_filter_type IN_OR_BLOOM_FILTER IN_OR_BLOOM_FILTER 0
+
+-- !cmd --
+experimental_enable_agg_state false false 0
+
+-- !cmd --
+show_hidden_columns false false 0
+
+-- !cmd --
+0
+
+-- !cmd --
+0
+
+-- !cmd --
+0
+
+-- !cmd --
+0
+
+-- !cmd --
+runtime_filter_type IN_OR_BLOOM_FILTER IN_OR_BLOOM_FILTER 0
+
+-- !cmd --
+experimental_enable_agg_state false false 0
+
+-- !cmd --
+show_hidden_columns false false 0
+
diff --git a/regression-test/suites/variable_p0/set_and_unset_variable.groovy
b/regression-test/suites/variable_p0/set_and_unset_variable.groovy
new file mode 100644
index 00000000000..53e5bb79bd9
--- /dev/null
+++ b/regression-test/suites/variable_p0/set_and_unset_variable.groovy
@@ -0,0 +1,72 @@
+// 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.
+
+suite("set_and_unset_variable") {
+
+ qt_cmd """set wait_timeout = 1000"""
+ qt_cmd """show variables like 'wait_timeout'"""
+ qt_cmd """UNSET VARIABLE wait_timeout"""
+ qt_cmd """show variables like 'wait_timeout'"""
+
+ qt_cmd """set runtime_filter_type='BLOOM_FILTER'"""
+ qt_cmd """show session variables like 'runtime_filter_type'"""
+ qt_cmd """show global variables like 'runtime_filter_type'"""
+ qt_cmd """UNSET VARIABLE runtime_filter_type"""
+ qt_cmd """show session variables like 'runtime_filter_type'"""
+ qt_cmd """show global variables like 'runtime_filter_type'"""
+
+ qt_cmd """set global runtime_filter_type='BLOOM_FILTER'"""
+ qt_cmd """show session variables like 'runtime_filter_type'"""
+ qt_cmd """show global variables like 'runtime_filter_type'"""
+ qt_cmd """UNSET global VARIABLE runtime_filter_type"""
+ qt_cmd """show session variables like 'runtime_filter_type'"""
+ qt_cmd """show global variables like 'runtime_filter_type'"""
+
+ // test variables with experimental_ prefix in session scope
+ qt_cmd """set experimental_enable_agg_state='true'"""
+ qt_cmd """show session variables like 'experimental_enable_agg_state'"""
+ qt_cmd """show global variables like 'experimental_enable_agg_state'"""
+ qt_cmd """UNSET VARIABLE experimental_enable_agg_state"""
+ qt_cmd """show session variables like 'experimental_enable_agg_state'"""
+ qt_cmd """show global variables like 'experimental_enable_agg_state'"""
+
+ // test variables with experimental_ prefix in global scope
+ qt_cmd """set global experimental_enable_agg_state='true'"""
+ qt_cmd """show session variables like 'experimental_enable_agg_state'"""
+ qt_cmd """show global variables like 'experimental_enable_agg_state'"""
+ qt_cmd """UNSET global VARIABLE experimental_enable_agg_state"""
+ qt_cmd """show session variables like 'experimental_enable_agg_state'"""
+ qt_cmd """show global variables like 'experimental_enable_agg_state'"""
+
+ // test UNSET VARIABLE ALL
+ qt_cmd """set runtime_filter_type='BLOOM_FILTER'"""
+ qt_cmd """set experimental_enable_agg_state='true'"""
+ qt_cmd """set show_hidden_columns=true"""
+ qt_cmd """UNSET VARIABLE ALL"""
+ qt_cmd """show session variables like 'runtime_filter_type'"""
+ qt_cmd """show session variables like 'experimental_enable_agg_state'"""
+ qt_cmd """show session variables like 'show_hidden_columns'"""
+
+ // test UNSET GLOBAL VARIABLE ALL
+ qt_cmd """set global runtime_filter_type='BLOOM_FILTER'"""
+ qt_cmd """set global experimental_enable_agg_state='true'"""
+ qt_cmd """set show_hidden_columns=true"""
+ qt_cmd """UNSET global VARIABLE ALL"""
+ qt_cmd """show global variables like 'runtime_filter_type'"""
+ qt_cmd """show global variables like 'experimental_enable_agg_state'"""
+ qt_cmd """show global variables like 'show_hidden_columns'"""
+}
\ No newline at end of file
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]