[bugfix](sessionVariable) Fix sessionVariable has sessionOriginValue property, and execute cloneSessionVariable method will throw java.io.NotSerializableException: java.lang.reflect.Field (#31029)
This commit is contained in:
@ -531,7 +531,7 @@ public class SessionVariable implements Serializable, Writable {
|
||||
public boolean enableStats = true;
|
||||
|
||||
// session origin value
|
||||
public Map<Field, String> sessionOriginValue = new HashMap<Field, String>();
|
||||
public Map<SessionVariableField, String> sessionOriginValue = new HashMap<>();
|
||||
// check stmt is or not [select /*+ SET_VAR(...)*/ ...]
|
||||
// if it is setStmt, we needn't collect session origin value
|
||||
public boolean isSingleSetVar = false;
|
||||
@ -2464,11 +2464,11 @@ public class SessionVariable implements Serializable, Writable {
|
||||
this.isSingleSetVar = issinglesetvar;
|
||||
}
|
||||
|
||||
public Map<Field, String> getSessionOriginValue() {
|
||||
public Map<SessionVariableField, String> getSessionOriginValue() {
|
||||
return sessionOriginValue;
|
||||
}
|
||||
|
||||
public void addSessionOriginValue(Field key, String value) {
|
||||
public void addSessionOriginValue(SessionVariableField key, String value) {
|
||||
if (sessionOriginValue.containsKey(key)) {
|
||||
// If we already set the origin value, just skip the reset.
|
||||
return;
|
||||
|
||||
@ -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.doris.qe;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Objects;
|
||||
|
||||
public class SessionVariableField implements Serializable {
|
||||
private transient Field field;
|
||||
|
||||
public SessionVariableField(Field field) {
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
public Field getField() {
|
||||
return field;
|
||||
}
|
||||
|
||||
private void writeObject(ObjectOutputStream out) throws IOException {
|
||||
out.defaultWriteObject();
|
||||
out.writeObject(field.getName());
|
||||
out.writeObject(field.getType());
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
in.defaultReadObject();
|
||||
String fieldName = (String) in.readObject();
|
||||
Class<?> fieldType = (Class<?>) in.readObject();
|
||||
try {
|
||||
field = getField(fieldName, fieldType);
|
||||
} catch (NoSuchFieldException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private Field getField(String fieldName, Class<?> fieldType) throws NoSuchFieldException {
|
||||
try {
|
||||
return SessionVariable.class.getDeclaredField(fieldName);
|
||||
} catch (NoSuchFieldException e) {
|
||||
Class<?> superclass = SessionVariable.class.getSuperclass();
|
||||
if (superclass != null) {
|
||||
return getField(fieldName, fieldType);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
SessionVariableField other = (SessionVariableField) obj;
|
||||
// 忽略 transient 字段的比较
|
||||
return Objects.equals(this.getField(), other.getField());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(this.getField());
|
||||
}
|
||||
|
||||
}
|
||||
@ -152,7 +152,11 @@ public class VariableMgr {
|
||||
}
|
||||
|
||||
// Set value to a variable
|
||||
private static boolean setValue(Object obj, Field field, String value) throws DdlException {
|
||||
private static boolean setValue(Object obj, SessionVariableField sessionVariableField, String value)
|
||||
throws DdlException {
|
||||
|
||||
Field field = sessionVariableField.getField();
|
||||
field.setAccessible(true);
|
||||
VarAttr attr = field.getAnnotation(VarAttr.class);
|
||||
|
||||
if (VariableVarConverters.hasConverter(attr.name())) {
|
||||
@ -196,22 +200,22 @@ public class VariableMgr {
|
||||
}
|
||||
break;
|
||||
case "byte":
|
||||
field.setByte(obj, Byte.valueOf(value));
|
||||
field.setByte(obj, Byte.parseByte(value));
|
||||
break;
|
||||
case "short":
|
||||
field.setShort(obj, Short.valueOf(value));
|
||||
field.setShort(obj, Short.parseShort(value));
|
||||
break;
|
||||
case "int":
|
||||
field.setInt(obj, Integer.valueOf(value));
|
||||
field.setInt(obj, Integer.parseInt(value));
|
||||
break;
|
||||
case "long":
|
||||
field.setLong(obj, Long.valueOf(value));
|
||||
field.setLong(obj, Long.parseLong(value));
|
||||
break;
|
||||
case "float":
|
||||
field.setFloat(obj, Float.valueOf(value));
|
||||
field.setFloat(obj, Float.parseFloat(value));
|
||||
break;
|
||||
case "double":
|
||||
field.setDouble(obj, Double.valueOf(value));
|
||||
field.setDouble(obj, Double.parseDouble(value));
|
||||
break;
|
||||
case "String":
|
||||
field.set(obj, value);
|
||||
@ -236,9 +240,9 @@ public class VariableMgr {
|
||||
|
||||
// revert the operator[set_var] on select/*+ SET_VAR()*/ sql;
|
||||
public static void revertSessionValue(SessionVariable obj) throws DdlException {
|
||||
Map<Field, String> sessionOriginValue = obj.getSessionOriginValue();
|
||||
Map<SessionVariableField, String> sessionOriginValue = obj.getSessionOriginValue();
|
||||
if (!sessionOriginValue.isEmpty()) {
|
||||
for (Field field : sessionOriginValue.keySet()) {
|
||||
for (SessionVariableField field : sessionOriginValue.keySet()) {
|
||||
// revert session value
|
||||
setValue(obj, field, sessionOriginValue.get(field));
|
||||
}
|
||||
@ -351,22 +355,24 @@ public class VariableMgr {
|
||||
|
||||
// No matter this is a global setting or not, always set session variable.
|
||||
Field field = ctx.getField();
|
||||
SessionVariableField sessionVariableField = new SessionVariableField(field);
|
||||
// if stmt is "Select /*+ SET_VAR(...)*/"
|
||||
if (sessionVariable.getIsSingleSetVar()) {
|
||||
try {
|
||||
sessionVariable.addSessionOriginValue(field, field.get(sessionVariable).toString());
|
||||
sessionVariable.addSessionOriginValue(sessionVariableField, field.get(sessionVariable).toString());
|
||||
} catch (Exception e) {
|
||||
LOG.warn("failed to collect origin session value ", e);
|
||||
}
|
||||
}
|
||||
setValue(sessionVariable, field, value);
|
||||
setValue(sessionVariable, sessionVariableField, value);
|
||||
}
|
||||
|
||||
private static void setGlobalVarAndWriteEditLog(VarContext ctx, String name, String value) throws DdlException {
|
||||
// global variable will make effect when is set immediately.
|
||||
wlock.lock();
|
||||
try {
|
||||
setValue(ctx.getObj(), ctx.getField(), value);
|
||||
|
||||
setValue(ctx.getObj(), new SessionVariableField(ctx.getField()), value);
|
||||
// write edit log
|
||||
GlobalVarPersistInfo info = new GlobalVarPersistInfo(defaultSessionVariable, Lists.newArrayList(name));
|
||||
Env.getCurrentEnv().getEditLog().logGlobalVariableV2(info);
|
||||
@ -380,7 +386,7 @@ public class VariableMgr {
|
||||
try {
|
||||
VarContext ctx = ctxByVarName.get(SessionVariable.PARALLEL_PIPELINE_TASK_NUM);
|
||||
try {
|
||||
setValue(ctx.getObj(), ctx.getField(), String.valueOf(instance));
|
||||
setValue(ctx.getObj(), new SessionVariableField(ctx.getField()), String.valueOf(instance));
|
||||
} catch (DdlException e) {
|
||||
LOG.warn("failed to set global variable: {}", SessionVariable.PARALLEL_PIPELINE_TASK_NUM, e);
|
||||
return;
|
||||
@ -400,7 +406,7 @@ public class VariableMgr {
|
||||
try {
|
||||
VarContext ctx = ctxByVarName.get(SessionVariable.BROADCAST_RIGHT_TABLE_SCALE_FACTOR);
|
||||
try {
|
||||
setValue(ctx.getObj(), ctx.getField(), String.valueOf(factor));
|
||||
setValue(ctx.getObj(), new SessionVariableField(ctx.getField()), String.valueOf(factor));
|
||||
} catch (DdlException e) {
|
||||
LOG.warn("failed to set global variable: {}", SessionVariable.BROADCAST_RIGHT_TABLE_SCALE_FACTOR, e);
|
||||
return;
|
||||
@ -420,7 +426,7 @@ public class VariableMgr {
|
||||
try {
|
||||
VarContext ctx = ctxByVarName.get(SessionVariable.ENABLE_NEREIDS_PLANNER);
|
||||
try {
|
||||
setValue(ctx.getObj(), ctx.getField(), String.valueOf(true));
|
||||
setValue(ctx.getObj(), new SessionVariableField(ctx.getField()), String.valueOf(true));
|
||||
} catch (DdlException e) {
|
||||
LOG.warn("failed to set global variable: {}", SessionVariable.ENABLE_NEREIDS_PLANNER, e);
|
||||
return;
|
||||
@ -440,7 +446,7 @@ public class VariableMgr {
|
||||
try {
|
||||
VarContext ctx = ctxByVarName.get(SessionVariable.ENABLE_NEREIDS_DML);
|
||||
try {
|
||||
setValue(ctx.getObj(), ctx.getField(), String.valueOf(true));
|
||||
setValue(ctx.getObj(), new SessionVariableField(ctx.getField()), String.valueOf(true));
|
||||
} catch (DdlException e) {
|
||||
LOG.warn("failed to set global variable: {}", SessionVariable.ENABLE_NEREIDS_DML, e);
|
||||
return;
|
||||
@ -511,7 +517,8 @@ public class VariableMgr {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
setValue(varContext.getObj(), varContext.getField(), root.get((String) varName).toString());
|
||||
setValue(varContext.getObj(), new SessionVariableField(varContext.getField()),
|
||||
root.get(varName).toString());
|
||||
} catch (Exception exception) {
|
||||
LOG.warn("Exception during replay global variabl {} oplog, {}, THIS EXCEPTION WILL BE IGNORED.",
|
||||
(String) varName, exception.getMessage());
|
||||
|
||||
@ -156,4 +156,16 @@ public class SessionVariablesTest extends TestWithFeService {
|
||||
Assertions.assertEquals(123, sessionVariable.getQueryTimeoutS());
|
||||
Assertions.assertEquals(123, sessionVariable.getInsertTimeoutS());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCloneSessionVariablesWithSessionOriginValueNotEmpty() throws NoSuchFieldException {
|
||||
Field txIsolation = SessionVariable.class.getField("txIsolation");
|
||||
SessionVariableField txIsolationSessionVariableField = new SessionVariableField(txIsolation);
|
||||
sessionVariable.addSessionOriginValue(txIsolationSessionVariableField, "test");
|
||||
|
||||
SessionVariable sessionVariableClone = VariableMgr.cloneSessionVariable(sessionVariable);
|
||||
|
||||
Assertions.assertEquals("test",
|
||||
sessionVariableClone.getSessionOriginValue().get(txIsolationSessionVariableField));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user