diff --git a/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java b/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java index 82ce628163..8bceb3fab7 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java +++ b/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java @@ -37,6 +37,7 @@ import java.util.Properties; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; @@ -247,6 +248,7 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements private boolean asyncCloseConnectionEnable = false; protected int maxCreateTaskCount = 3; protected boolean failFast = false; + protected AtomicBoolean failContinuous = new AtomicBoolean(false); protected ScheduledExecutorService destroyScheduler; protected ScheduledExecutorService createScheduler; @@ -1516,6 +1518,10 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements lock.unlock(); } } + + protected void setFailContinuous(boolean fail) { + failContinuous.set(fail); + } public void initPhysicalConnection(Connection conn) throws SQLException { if (conn.getAutoCommit() != defaultAutoCommit) { diff --git a/src/main/java/com/alibaba/druid/pool/DruidDataSource.java b/src/main/java/com/alibaba/druid/pool/DruidDataSource.java index 8c664106cc..9bdd63cb4a 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidDataSource.java +++ b/src/main/java/com/alibaba/druid/pool/DruidDataSource.java @@ -1509,7 +1509,7 @@ public class DruidDataSource extends DruidAbstractDataSource while (poolingCount == 0) { emptySignal(); // send signal to CreateThread create connection - if (failFast && createError != null) { + if (failFast && failContinuous.get()) { throw new DataSourceNotAvailableException(createError); } @@ -1549,7 +1549,7 @@ public class DruidDataSource extends DruidAbstractDataSource if (poolingCount == 0) { emptySignal(); // send signal to CreateThread create connection - if (failFast && createError != null) { + if (failFast && failContinuous.get()) { throw new DataSourceNotAvailableException(createError); } @@ -1885,21 +1885,23 @@ public class DruidDataSource extends DruidAbstractDataSource try { physicalConnection = createPhysicalConnection(); + setFailContinuous(false); } catch (SQLException e) { LOG.error("create connection error, url: " + jdbcUrl, e); errorCount++; - - if (failFast) { - lock.lock(); - try { - notEmpty.signalAll(); - } finally { - lock.unlock(); - } - } - if (errorCount > connectionErrorRetryAttempts && timeBetweenConnectErrorMillis > 0) { + // fail over retry attempts + setFailContinuous(true); + if (failFast) { + lock.lock(); + try { + notEmpty.signalAll(); + } finally { + lock.unlock(); + } + } + if (breakAfterAcquireFailure) { lock.lock(); try { @@ -1916,6 +1918,8 @@ public class DruidDataSource extends DruidAbstractDataSource } } catch (RuntimeException e) { LOG.error("create connection error", e); + // unknow fatal exception + setFailContinuous(true); continue; } catch (Error e) { lock.lock(); @@ -1925,6 +1929,8 @@ public class DruidDataSource extends DruidAbstractDataSource lock.unlock(); } LOG.error("create connection error", e); + // unknow fatal exception + setFailContinuous(true); break; } @@ -1993,21 +1999,23 @@ public class DruidDataSource extends DruidAbstractDataSource try { connection = createPhysicalConnection(); + setFailContinuous(false); } catch (SQLException e) { LOG.error("create connection error, url: " + jdbcUrl + ", errorCode " + e.getErrorCode() + ", state " + e.getSQLState(), e); errorCount++; - - if (failFast) { - lock.lock(); - try { - notEmpty.signalAll(); - } finally { - lock.unlock(); - } - } - if (errorCount > connectionErrorRetryAttempts && timeBetweenConnectErrorMillis > 0) { + // fail over retry attempts + setFailContinuous(true); + if (failFast) { + lock.lock(); + try { + notEmpty.signalAll(); + } finally { + lock.unlock(); + } + } + if (breakAfterAcquireFailure) { break; } @@ -2020,9 +2028,11 @@ public class DruidDataSource extends DruidAbstractDataSource } } catch (RuntimeException e) { LOG.error("create connection error", e); + setFailContinuous(true); continue; } catch (Error e) { LOG.error("create connection error", e); + setFailContinuous(true); break; } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlStatementParser.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlStatementParser.java index 6bba3f5f04..e5bce2cc74 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlStatementParser.java @@ -2442,23 +2442,43 @@ public class MySqlStatementParser extends SQLStatementParser { stmt.addItem(item); } else if (identifierEquals("MODIFY")) { lexer.nextToken(); + if (lexer.token() == Token.COLUMN) { lexer.nextToken(); } - MySqlAlterTableModifyColumn item = new MySqlAlterTableModifyColumn(); - item.setNewColumnDefinition(this.exprParser.parseColumn()); - if (identifierEquals("AFTER")) { - lexer.nextToken(); - item.setAfterColumn(this.exprParser.name()); - } else if (identifierEquals("FIRST")) { - lexer.nextToken(); - if (lexer.token() == Token.IDENTIFIER) { - item.setFirstColumn(this.exprParser.name()); - } else { - item.setFirst(true); - } + + boolean paren = false; + if (lexer.token() == Token.LPAREN) { + paren = true; + lexer.nextToken(); + } + + for (;;) { + MySqlAlterTableModifyColumn item = new MySqlAlterTableModifyColumn(); + item.setNewColumnDefinition(this.exprParser.parseColumn()); + if (identifierEquals("AFTER")) { + lexer.nextToken(); + item.setAfterColumn(this.exprParser.name()); + } else if (identifierEquals("FIRST")) { + lexer.nextToken(); + if (lexer.token() == Token.IDENTIFIER) { + item.setFirstColumn(this.exprParser.name()); + } else { + item.setFirst(true); + } + } + stmt.addItem(item); + + if (paren && lexer.token() == Token.COMMA) { + lexer.nextToken(); + continue; + } + break; + } + + if (paren) { + accept(Token.RPAREN); } - stmt.addItem(item); } else if (lexer.token() == Token.DISABLE) { lexer.nextToken(); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java index 0cce4a8d54..829ddcb2d3 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java @@ -299,8 +299,12 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV public boolean visit(SQLColumnDefinition x) { x.getName().accept(this); - print(' '); - x.getDataType().accept(this); + + SQLDataType dataType = x.getDataType(); + if (dataType != null) { + print(' '); + dataType.accept(this); + } if (x.getCharsetExpr() != null) { print0(ucase ? " CHARSET " : " charset "); diff --git a/src/test/java/com/alibaba/druid/bvt/pool/profile/FailFastTest.java b/src/test/java/com/alibaba/druid/bvt/pool/profile/FailFastTest.java index 8db142a7f4..d2537d7aa6 100644 --- a/src/test/java/com/alibaba/druid/bvt/pool/profile/FailFastTest.java +++ b/src/test/java/com/alibaba/druid/bvt/pool/profile/FailFastTest.java @@ -8,6 +8,7 @@ import java.util.concurrent.atomic.AtomicReference; import org.junit.Assert; +import com.alibaba.druid.pool.DataSourceNotAvailableException; import com.alibaba.druid.pool.DruidDataSource; import junit.framework.TestCase; @@ -70,8 +71,9 @@ public class FailFastTest extends TestCase { connectStartLatch.await(); latch.countDown(); - connectEndLatch.await(3, TimeUnit.SECONDS); + SQLException ex = errorHolder.get(); + Assert.assertTrue(ex instanceof DataSourceNotAvailableException); } } diff --git a/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlAlterTableTest23.java b/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlAlterTableTest23.java new file mode 100644 index 0000000000..7f4fe01622 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlAlterTableTest23.java @@ -0,0 +1,38 @@ +/* + * Copyright 1999-2101 Alibaba Group Holding Ltd. + * + * Licensed 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 com.alibaba.druid.bvt.sql.mysql; + +import junit.framework.TestCase; + +import org.junit.Assert; + +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; +import com.alibaba.druid.sql.parser.Token; + +public class MySqlAlterTableTest23 extends TestCase { + + public void test_alter_add_key() throws Exception { + String sql = "alter table xxxxx modify (xxx default '0');"; + MySqlStatementParser parser = new MySqlStatementParser(sql); + SQLStatement stmt = parser.parseStatementList().get(0); + parser.match(Token.EOF); + String output = SQLUtils.toMySqlString(stmt); + Assert.assertEquals("ALTER TABLE xxxxx" + + "\n\tMODIFY COLUMN xxx DEFAULT '0'", output); + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlAlterTableTest24.java b/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlAlterTableTest24.java new file mode 100644 index 0000000000..8c79518831 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlAlterTableTest24.java @@ -0,0 +1,39 @@ +/* + * Copyright 1999-2101 Alibaba Group Holding Ltd. + * + * Licensed 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 com.alibaba.druid.bvt.sql.mysql; + +import junit.framework.TestCase; + +import org.junit.Assert; + +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; +import com.alibaba.druid.sql.parser.Token; + +public class MySqlAlterTableTest24 extends TestCase { + + public void test_alter_add_key() throws Exception { + String sql = "alter table xxxxx modify (f0 default '0',f1 default '0');"; + MySqlStatementParser parser = new MySqlStatementParser(sql); + SQLStatement stmt = parser.parseStatementList().get(0); + parser.match(Token.EOF); + String output = SQLUtils.toMySqlString(stmt); + Assert.assertEquals("ALTER TABLE xxxxx" + + "\n\tMODIFY COLUMN f0 DEFAULT '0'," + + "\n\tMODIFY COLUMN f1 DEFAULT '0'", output); + } +}