mirror of
https://github.com/actiontech/dble.git
synced 2026-05-25 01:09:29 -05:00
Merge branch '2.17.09.0/rel'
This commit is contained in:
@@ -320,6 +320,21 @@ public final class CharsetUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static int getCollationIndexByCharset(String charset, String collation) {
|
||||
if (collation == null || collation.length() == 0) {
|
||||
return 0;
|
||||
} else {
|
||||
CollationInfo info = COLLATION_TO_INDEX.get(collation.toLowerCase());
|
||||
if (info == null) {
|
||||
return 0;
|
||||
} else if (!info.charset.equals(charset)) {
|
||||
return -1;
|
||||
} else {
|
||||
return info.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int getCharsetDefaultIndex(String charset) {
|
||||
if (charset == null || charset.length() == 0) {
|
||||
return 0;
|
||||
|
||||
@@ -537,5 +537,6 @@ public final class ErrorCode {
|
||||
public static final int ER_PLUGIN_IS_NOT_LOADED = 1494;
|
||||
public static final int ER_USER_READ_ONLY = 1495;
|
||||
public static final int CREATE_VIEW_ERROR = 1999;
|
||||
public static final int ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION = 1792;
|
||||
|
||||
}
|
||||
|
||||
@@ -28,9 +28,14 @@ public class ManagerQueryHandler implements FrontendQueryHandler {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadOnly(Boolean readOnly) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSessionReadOnly(boolean sessionReadOnly) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void query(String sql) {
|
||||
ManagerConnection c = this.source;
|
||||
|
||||
@@ -309,7 +309,8 @@ public abstract class FrontendConnection extends AbstractConnection {
|
||||
|
||||
// execute
|
||||
if (queryHandler != null) {
|
||||
queryHandler.setReadOnly(userReadOnly || sessionReadOnly);
|
||||
queryHandler.setReadOnly(userReadOnly);
|
||||
queryHandler.setSessionReadOnly(sessionReadOnly);
|
||||
queryHandler.query(sql);
|
||||
} else {
|
||||
writeErrMessage(ErrorCode.ER_UNKNOWN_COM_ERROR, "Query unsupported!");
|
||||
|
||||
@@ -15,4 +15,6 @@ public interface FrontendQueryHandler {
|
||||
void query(String sql);
|
||||
|
||||
void setReadOnly(Boolean readOnly);
|
||||
|
||||
void setSessionReadOnly(boolean sessionReadOnly);
|
||||
}
|
||||
|
||||
@@ -20,12 +20,18 @@ public class ServerQueryHandler implements FrontendQueryHandler {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ServerQueryHandler.class);
|
||||
|
||||
private final ServerConnection source;
|
||||
protected Boolean readOnly = true;
|
||||
|
||||
private Boolean readOnly = true;
|
||||
private boolean sessionReadOnly = true;
|
||||
@Override
|
||||
public void setReadOnly(Boolean readOnly) {
|
||||
this.readOnly = readOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSessionReadOnly(boolean sessionReadOnly) {
|
||||
this.sessionReadOnly = sessionReadOnly;
|
||||
}
|
||||
|
||||
public ServerQueryHandler(ServerConnection source) {
|
||||
this.source = source;
|
||||
}
|
||||
@@ -119,8 +125,10 @@ public class ServerQueryHandler implements FrontendQueryHandler {
|
||||
break;
|
||||
default:
|
||||
if (readOnly) {
|
||||
LOGGER.warn("User readonly:" + sql);
|
||||
c.writeErrMessage(ErrorCode.ER_USER_READ_ONLY, "User readonly");
|
||||
c.writeErrMessage(ErrorCode.ER_USER_READ_ONLY, "User READ ONLY");
|
||||
break;
|
||||
} else if (sessionReadOnly) {
|
||||
c.writeErrMessage(ErrorCode.ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION, "Cannot execute statement in a READ ONLY transaction.");
|
||||
break;
|
||||
}
|
||||
c.execute(sql, rs & 0xff);
|
||||
|
||||
@@ -31,6 +31,8 @@ import com.alibaba.druid.sql.parser.SQLStatementParser;
|
||||
import java.sql.SQLSyntaxErrorException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* SetHandler
|
||||
@@ -87,6 +89,7 @@ public final class SetHandler {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean handleSetStatement(String stmt, ServerConnection c, List<Pair<KeyType, Pair<String, String>>> contextTask) throws SQLSyntaxErrorException {
|
||||
SQLStatement statement = parseSQL(stmt);
|
||||
if (statement instanceof SQLSetStatement) {
|
||||
@@ -99,8 +102,8 @@ public final class SetHandler {
|
||||
} else if (statement instanceof MySqlSetNamesStatement) {
|
||||
MySqlSetNamesStatement setNamesStatement = (MySqlSetNamesStatement) statement;
|
||||
if (contextTask.size() > 0 || stmt.contains(",")) {
|
||||
if (handleSetNamesInMultiStmt(c, setNamesStatement.getCharSet(), setNamesStatement.getCollate(), contextTask)) {
|
||||
int index = stmt.indexOf(",");
|
||||
int index = stmt.indexOf(",");
|
||||
if (handleSetNamesInMultiStmt(c, stmt.substring(0, index), setNamesStatement.isDefault(), setNamesStatement.getCharSet(), setNamesStatement.getCollate(), contextTask)) {
|
||||
String newStmt = "set " + stmt.substring(index + 1);
|
||||
return handleSetStatement(newStmt, c, contextTask);
|
||||
} else {
|
||||
@@ -112,7 +115,7 @@ public final class SetHandler {
|
||||
} else if (statement instanceof MySqlSetCharSetStatement) {
|
||||
MySqlSetCharSetStatement setCharSetStatement = (MySqlSetCharSetStatement) statement;
|
||||
if (contextTask.size() > 0 || stmt.contains(",")) {
|
||||
if (handleCharsetInMultiStmt(c, setCharSetStatement.getCharSet(), contextTask)) {
|
||||
if (handleCharsetInMultiStmt(c, setCharSetStatement.isDefault(), setCharSetStatement.getCharSet(), contextTask)) {
|
||||
int index = stmt.indexOf(",");
|
||||
String newStmt = "set " + stmt.substring(index + 1);
|
||||
return handleSetStatement(newStmt, c, contextTask);
|
||||
@@ -130,31 +133,48 @@ public final class SetHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean handleSetNamesInMultiStmt(ServerConnection c, String charset, String collate, List<Pair<KeyType, Pair<String, String>>> contextTask) {
|
||||
String[] charsetInfo = checkSetNames(charset, collate);
|
||||
private static boolean handleSetNamesInMultiStmt(ServerConnection c, String stmt, boolean isDefault, String charset, String collate, List<Pair<KeyType, Pair<String, String>>> contextTask) {
|
||||
String[] charsetInfo = checkSetNames(stmt, isDefault, charset, collate);
|
||||
if (charsetInfo != null) {
|
||||
contextTask.add(new Pair<>(KeyType.NAMES, new Pair<>(charsetInfo[0], charsetInfo[1])));
|
||||
return true;
|
||||
if (charsetInfo[0] == null) {
|
||||
c.writeErrMessage(ErrorCode.ER_UNKNOWN_CHARACTER_SET, "Unknown character set '" + charset + " or collate '" + collate + "'");
|
||||
return false;
|
||||
}
|
||||
if (charsetInfo[1] == null) {
|
||||
c.writeErrMessage(ErrorCode.ER_COLLATION_CHARSET_MISMATCH, "COLLATION '" + collate + "' is not valid for CHARACTER SET '" + charset + "'");
|
||||
return false;
|
||||
} else {
|
||||
contextTask.add(new Pair<>(KeyType.NAMES, new Pair<>(charsetInfo[0], charsetInfo[1])));
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
c.writeErrMessage(ErrorCode.ER_UNKNOWN_CHARACTER_SET, "Unknown character set '" + charset + " or collate '" + collate + "'");
|
||||
c.writeErrMessage(ErrorCode.ER_PARSE_ERROR, "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the SQL: " + stmt);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean handleSingleSetNames(String stmt, ServerConnection c, MySqlSetNamesStatement statement) {
|
||||
String[] charsetInfo = checkSetNames(statement.getCharSet(), statement.getCollate());
|
||||
String[] charsetInfo = checkSetNames(stmt, statement.isDefault(), statement.getCharSet(), statement.getCollate());
|
||||
if (charsetInfo != null) {
|
||||
c.setNames(charsetInfo[0], charsetInfo[1]);
|
||||
c.write(c.writeToBuffer(OkPacket.OK, c.allocate()));
|
||||
return true;
|
||||
if (charsetInfo[0] == null) {
|
||||
c.writeErrMessage(ErrorCode.ER_UNKNOWN_CHARACTER_SET, "Unknown character set in statement '" + stmt + "");
|
||||
return false;
|
||||
} else if (charsetInfo[1] == null) {
|
||||
c.writeErrMessage(ErrorCode.ER_COLLATION_CHARSET_MISMATCH, "COLLATION '" + statement.getCollate() + "' is not valid for CHARACTER SET '" + statement.getCharSet() + "'");
|
||||
return false;
|
||||
} else {
|
||||
c.setNames(charsetInfo[0], charsetInfo[1]);
|
||||
c.write(c.writeToBuffer(OkPacket.OK, c.allocate()));
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
c.writeErrMessage(ErrorCode.ER_UNKNOWN_CHARACTER_SET, "Unknown character set in statement '" + stmt + "");
|
||||
c.writeErrMessage(ErrorCode.ER_PARSE_ERROR, "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the SQL: " + stmt);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean handleSingleSetCharset(String stmt, ServerConnection c, MySqlSetCharSetStatement statement) {
|
||||
String charset = getCharset(statement.getCharSet());
|
||||
String charset = getCharset(statement.isDefault(), statement.getCharSet());
|
||||
if (charset != null) {
|
||||
c.setCharacterSet(charset);
|
||||
c.write(c.writeToBuffer(OkPacket.OK, c.allocate()));
|
||||
@@ -203,12 +223,13 @@ public final class SetHandler {
|
||||
case NAMES: {
|
||||
String charset = parseStringValue(valueExpr);
|
||||
//TODO:druid lost collation info
|
||||
if (!handleSetNamesInMultiStmt(c, charset, null, contextTask)) return false;
|
||||
if (!handleSetNamesInMultiStmt(c, "SET NAMES " + charset, false, charset, null, contextTask))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
case CHARSET: {
|
||||
String charset = parseStringValue(valueExpr);
|
||||
if (!handleCharsetInMultiStmt(c, charset, contextTask)) return false;
|
||||
if (!handleCharsetInMultiStmt(c, false, charset, contextTask)) return false;
|
||||
break;
|
||||
}
|
||||
case CHARACTER_SET_CLIENT:
|
||||
@@ -248,8 +269,8 @@ public final class SetHandler {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean handleCharsetInMultiStmt(ServerConnection c, String charset, List<Pair<KeyType, Pair<String, String>>> contextTask) {
|
||||
String charsetInfo = getCharset(charset);
|
||||
private static boolean handleCharsetInMultiStmt(ServerConnection c, boolean isDefault, String charset, List<Pair<KeyType, Pair<String, String>>> contextTask) {
|
||||
String charsetInfo = getCharset(isDefault, charset);
|
||||
if (charsetInfo != null) {
|
||||
contextTask.add(new Pair<>(KeyType.CHARSET, new Pair<String, String>(charsetInfo, null)));
|
||||
return true;
|
||||
@@ -359,8 +380,16 @@ public final class SetHandler {
|
||||
case COLLATION_CONNECTION:
|
||||
return handleCollationConnection(c, valueExpr);
|
||||
case TX_READ_ONLY:
|
||||
if (!stmt.toLowerCase().contains("session")) {
|
||||
c.writeErrMessage(ErrorCode.ERR_NOT_SUPPORTED, "setting transaction without any SESSION or GLOBAL keyword is not supported now");
|
||||
return false;
|
||||
}
|
||||
return handleTxReadOnly(c, valueExpr);
|
||||
case TX_ISOLATION:
|
||||
if (!stmt.toLowerCase().contains("session")) {
|
||||
c.writeErrMessage(ErrorCode.ERR_NOT_SUPPORTED, "setting transaction without any SESSION or GLOBAL keyword is not supported now");
|
||||
return false;
|
||||
}
|
||||
return handleTxIsolation(c, valueExpr);
|
||||
case SYSTEM_VARIABLES:
|
||||
if (key.startsWith("@@")) {
|
||||
@@ -580,7 +609,8 @@ public final class SetHandler {
|
||||
|
||||
private static boolean checkValue(SQLExpr valueExpr) {
|
||||
return (valueExpr instanceof SQLCharExpr) || (valueExpr instanceof SQLIdentifierExpr) ||
|
||||
(valueExpr instanceof SQLIntegerExpr) || (valueExpr instanceof SQLNumberExpr) || (valueExpr instanceof SQLBooleanExpr);
|
||||
(valueExpr instanceof SQLIntegerExpr) || (valueExpr instanceof SQLNumberExpr) ||
|
||||
(valueExpr instanceof SQLBooleanExpr) || (valueExpr instanceof SQLDefaultExpr);
|
||||
}
|
||||
|
||||
private static KeyType parseKeyType(String key, boolean origin, KeyType defaultVariables) {
|
||||
@@ -660,6 +690,9 @@ public final class SetHandler {
|
||||
} else if (valueExpr instanceof SQLBooleanExpr) {
|
||||
SQLBooleanExpr value = (SQLBooleanExpr) valueExpr;
|
||||
strValue = String.valueOf(value.getValue());
|
||||
} else if (valueExpr instanceof SQLDefaultExpr) {
|
||||
SQLDefaultExpr value = (SQLDefaultExpr) valueExpr;
|
||||
strValue = value.toString();
|
||||
}
|
||||
return strValue;
|
||||
}
|
||||
@@ -675,6 +708,9 @@ public final class SetHandler {
|
||||
} else if (valueExpr instanceof SQLIntegerExpr) {
|
||||
SQLIntegerExpr value = (SQLIntegerExpr) valueExpr;
|
||||
strValue = value.getNumber().toString();
|
||||
} else if (valueExpr instanceof SQLDefaultExpr) {
|
||||
SQLDefaultExpr value = (SQLDefaultExpr) valueExpr;
|
||||
strValue = value.toString();
|
||||
}
|
||||
return strValue;
|
||||
}
|
||||
@@ -731,26 +767,36 @@ public final class SetHandler {
|
||||
return ci > 0;
|
||||
}
|
||||
|
||||
private static String getCharset(String charset) {
|
||||
charset = charset.toLowerCase();
|
||||
if (charset.equals("default")) {
|
||||
private static String getCharset(boolean isDefault, String charset) {
|
||||
if (isDefault || charset.toLowerCase().equals("default")) {
|
||||
charset = DbleServer.getInstance().getConfig().getSystem().getCharset();
|
||||
}
|
||||
charset = StringUtil.removeApostropheOrBackQuote(charset);
|
||||
charset = StringUtil.removeApostropheOrBackQuote(charset.toLowerCase());
|
||||
if (checkCharset(charset)) {
|
||||
return charset;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String[] checkSetNames(String charset, String collate) {
|
||||
charset = charset.toLowerCase();
|
||||
if (charset.equals("default")) {
|
||||
|
||||
private static boolean checkSetNamesSyntax(String stmt) {
|
||||
//druid parser can't find syntax error,use regex to check again, but it is not strict
|
||||
String regex = "^\\s*set\\s+names\\s+[`\\']?[a-zA-Z_0-9]+[`\\']?(\\s+collate\\s+[`\\']?[a-zA-Z_0-9]+[`\\']?)?;?\\s*$";
|
||||
Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
|
||||
Matcher ma = pattern.matcher(stmt);
|
||||
return ma.matches();
|
||||
}
|
||||
|
||||
private static String[] checkSetNames(String stmt, boolean isDefault, String charset, String collate) {
|
||||
if (collate == null && !(checkSetNamesSyntax(stmt))) {
|
||||
return null;
|
||||
}
|
||||
if (isDefault || charset.toLowerCase().equals("default")) {
|
||||
charset = DbleServer.getInstance().getConfig().getSystem().getCharset();
|
||||
} else {
|
||||
charset = StringUtil.removeApostropheOrBackQuote(charset);
|
||||
charset = StringUtil.removeApostropheOrBackQuote(charset.toLowerCase());
|
||||
if (!checkCharset(charset)) {
|
||||
return null;
|
||||
return new String[]{null, null};
|
||||
}
|
||||
}
|
||||
if (collate == null) {
|
||||
@@ -759,8 +805,13 @@ public final class SetHandler {
|
||||
collate = collate.toLowerCase();
|
||||
if (collate.equals("default")) {
|
||||
collate = CharsetUtil.getDefaultCollation(charset);
|
||||
} else if (CharsetUtil.getCollationIndex(collate) <= 0) {
|
||||
return null;
|
||||
} else {
|
||||
int collateIndex = CharsetUtil.getCollationIndexByCharset(charset, collate);
|
||||
if (collateIndex == 0) {
|
||||
return new String[]{null, null};
|
||||
} else if (collateIndex < 0) {
|
||||
return new String[]{charset, null};
|
||||
}
|
||||
}
|
||||
}
|
||||
return new String[]{charset, collate};
|
||||
|
||||
@@ -12,8 +12,6 @@ import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class SetHandlerTest {
|
||||
|
||||
|
||||
@Test
|
||||
public void testConvertCharsetKeyWord() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Method convertCharsetKeyWord = SetHandler.class.getDeclaredMethod("convertCharsetKeyWord", String.class);
|
||||
@@ -23,4 +21,25 @@ public class SetHandlerTest {
|
||||
Assert.assertEquals("SET names utf8,character set UTF8,character set gbk,@@tx_readonly=1", convertCharsetKeyWord.invoke(null, "SET names utf8,CHARSET UTF8,CHARSET gbk,@@tx_readonly=1"));
|
||||
Assert.assertEquals("SET names utf8,character set UTF8,character set gbk,@@tx_readonly=1", convertCharsetKeyWord.invoke(null, "SET names utf8,CHARSET UTF8,CHARSET gbk,@@tx_readonly=1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCheckSetNamesSyntax() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Method checkSetNamesSyntax = SetHandler.class.getDeclaredMethod("checkSetNamesSyntax", String.class);
|
||||
checkSetNamesSyntax.setAccessible(true);
|
||||
Assert.assertEquals(true, checkSetNamesSyntax.invoke(null, "SET NAMES utf8"));
|
||||
Assert.assertEquals(true, checkSetNamesSyntax.invoke(null, "set names utf8"));
|
||||
Assert.assertEquals(true, checkSetNamesSyntax.invoke(null, "set names 'utf8'"));
|
||||
Assert.assertEquals(true, checkSetNamesSyntax.invoke(null, "set names `utf8`"));
|
||||
Assert.assertEquals(true, checkSetNamesSyntax.invoke(null, "set names DEFAULT"));
|
||||
Assert.assertEquals(true, checkSetNamesSyntax.invoke(null, "set names utf8 COLLATE utf8_bin"));
|
||||
Assert.assertEquals(true, checkSetNamesSyntax.invoke(null, "set names utf8 COLLATE default"));
|
||||
Assert.assertEquals(true, checkSetNamesSyntax.invoke(null, "set names 'utf8' COLLATE utf8_bin"));
|
||||
Assert.assertEquals(true, checkSetNamesSyntax.invoke(null, "set names `utf8` COLLATE default"));
|
||||
Assert.assertEquals(true, checkSetNamesSyntax.invoke(null, "set names utf8 COLLATE 'utf8_bin'"));
|
||||
Assert.assertEquals(true, checkSetNamesSyntax.invoke(null, "set names utf8 COLLATE `utf8_bin`"));
|
||||
Assert.assertEquals(true, checkSetNamesSyntax.invoke(null, "set names 'utf8' COLLATE 'utf8_bin'"));
|
||||
Assert.assertEquals(true, checkSetNamesSyntax.invoke(null, "set names `utf8` COLLATE `utf8_bin`"));
|
||||
Assert.assertEquals(false, checkSetNamesSyntax.invoke(null, "set names utf8 2"));
|
||||
Assert.assertEquals(false, checkSetNamesSyntax.invoke(null, "set names utf8 COLLATION utf8_bin"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user