improved mysql/oracle sql parser.

This commit is contained in:
wenshao
2017-05-20 22:03:21 +08:00
parent ae0ae2e9e0
commit ac4b6059f0
53 changed files with 1449 additions and 296 deletions
@@ -28,23 +28,26 @@ public class MySqlCreateTableTest76 extends MysqlTest {
@Test
public void test_one() throws Exception {
String sql = "CREATE TABLE `log_info_20170516` (\n" +
" `toid` varchar(40) CHARACTER SET utf8 NOT NULL,\n" +
" `title` varchar(40) CHARACTER SET utf8 DEFAULT NULL,\n" +
" `traceid` varchar(50) CHARACTER SET utf8 DEFAULT NULL,\n" +
" `parentid` varchar(40) CHARACTER SET utf8 DEFAULT NULL,\n" +
" `key1` varchar(100) CHARACTER SET utf8 DEFAULT NULL,\n" +
" `key2` varchar(100) CHARACTER SET utf8 DEFAULT NULL,\n" +
" `key3` varchar(100) CHARACTER SET utf8 DEFAULT NULL,\n" +
" `type` varchar(50) CHARACTER SET utf8 DEFAULT NULL,\n" +
" `createdate` varchar(30) CHARACTER SET utf8 DEFAULT NULL,\n" +
" `classmethod` varchar(100) CHARACTER SET utf8 DEFAULT NULL,\n" +
" `linenum` int(11) DEFAULT NULL,\n" +
" `threadname` varchar(80) CHARACTER SET utf8 DEFAULT '',\n" +
" `content` text CHARACTER SET utf8,\n" +
" PRIMARY KEY (`toid`),\n" +
" KEY `traceid_index` (`traceid`)\n" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;";
String sql = "CREATE TABLE `sys_msg_entry_0320` (\n" +
" `provider_dsp_name` varchar(256) /*!50616 COLUMN_FORMAT COMPRESSED */ DEFAULT NULL COMMENT '消息提供者的显示名,提供给一些需要统一发送名称的系统消息',\n" +
" `template_data` varchar(2048) NOT NULL /*!50616 COLUMN_FORMAT COMPRESSED */ COMMENT '模板渲染时使用的数据,key-value对',\n" +
" `template_merge_data` varchar(512) /*!50616 COLUMN_FORMAT COMPRESSED */ DEFAULT NULL COMMENT '对于需要合并的消息,被合并的参数放在此处理',\n" +
" `expiration_date` datetime NOT NULL COMMENT '失效日期,精度到天,查询和任务处理时使用',\n" +
" `merge_key` varchar(128) DEFAULT NULL COMMENT '消息合并主键,合并成功的消息,不参与计数',\n" +
" `out_id` varchar(256) /*!50616 COLUMN_FORMAT COMPRESSED */ DEFAULT NULL COMMENT '外部关联源的id',\n" +
" `number_expiration_date` datetime DEFAULT NULL COMMENT '计数过期时间',\n" +
" `hidden` int(11) DEFAULT '0' COMMENT '标记是否在吊顶展示,1表示隐藏,0表示展示',\n" +
" `popup` tinyint(3) unsigned DEFAULT '0' COMMENT '表示消息提醒方式:0-数字提醒,1-layer,2-popup',\n" +
" `tag_id` bigint(20) unsigned DEFAULT NULL COMMENT 'TAG标志',\n" +
" `attribute` varchar(512) /*!50616 COLUMN_FORMAT COMPRESSED */ DEFAULT NULL COMMENT '消息的一些属性',\n" +
" `original_msg_ids` varchar(1024) /*!50616 COLUMN_FORMAT COMPRESSED */ DEFAULT NULL COMMENT '原始消息ID,多个id用半角逗号隔开',\n" +
" PRIMARY KEY (`id`),\n" +
" KEY `idx_expiration_date` (`expiration_date`),\n" +
" KEY `k_rid_hidd_cid` (`receiver_id`,`hidden`,`cat_id`,`expiration_date`),\n" +
" KEY `k_rid_aid_tid` (`receiver_id`,`app_id`,`type_id`,`expiration_date`),\n" +
" KEY `k_rid_stat_popup` (`receiver_id`,`status`,`popup`,`expiration_date`),\n" +
" KEY `k_tid` (`tag_id`)\n" +
") ENGINE=InnoDB AUTO_INCREMENT=167279613030 DEFAULT CHARSET=gbk COMMENT='消息实体表'";
MySqlStatementParser parser = new MySqlStatementParser(sql);
SQLStatement stmt = parser.parseCreateTable();
@@ -52,50 +55,56 @@ public class MySqlCreateTableTest76 extends MysqlTest {
MySqlSchemaStatVisitor visitor = new MySqlSchemaStatVisitor();
stmt.accept(visitor);
Column column = visitor.getColumn("log_info_20170516", "toid");
Column column = visitor.getColumn("sys_msg_entry_0320", "provider_dsp_name");
Assert.assertNotNull(column);
Assert.assertEquals("varchar", column.getDataType());
{
String output = SQLUtils.toMySqlString(stmt);
Assert.assertEquals("CREATE TABLE `log_info_20170516` (\n" +
"\t`toid` varchar(40) CHARACTER SET utf8 NOT NULL, \n" +
"\t`title` varchar(40) CHARACTER SET utf8 DEFAULT NULL, \n" +
"\t`traceid` varchar(50) CHARACTER SET utf8 DEFAULT NULL, \n" +
"\t`parentid` varchar(40) CHARACTER SET utf8 DEFAULT NULL, \n" +
"\t`key1` varchar(100) CHARACTER SET utf8 DEFAULT NULL, \n" +
"\t`key2` varchar(100) CHARACTER SET utf8 DEFAULT NULL, \n" +
"\t`key3` varchar(100) CHARACTER SET utf8 DEFAULT NULL, \n" +
"\t`type` varchar(50) CHARACTER SET utf8 DEFAULT NULL, \n" +
"\t`createdate` varchar(30) CHARACTER SET utf8 DEFAULT NULL, \n" +
"\t`classmethod` varchar(100) CHARACTER SET utf8 DEFAULT NULL, \n" +
"\t`linenum` int(11) DEFAULT NULL, \n" +
"\t`threadname` varchar(80) CHARACTER SET utf8 DEFAULT '', \n" +
"\t`content` text CHARACTER SET utf8, \n" +
"\tPRIMARY KEY (`toid`), \n" +
"\tKEY `traceid_index` (`traceid`)\n" +
") ENGINE = InnoDB CHARSET = utf8mb4", output);
Assert.assertEquals("CREATE TABLE `sys_msg_entry_0320` (\n" +
"\t`provider_dsp_name` varchar(256) /*!50616 COLUMN_FORMAT COMPRESSED */ DEFAULT NULL COMMENT '消息提供者的显示名,提供给一些需要统一发送名称的系统消息', \n" +
"\t`template_data` varchar(2048) NOT NULL /*!50616 COLUMN_FORMAT COMPRESSED */ COMMENT '模板渲染时使用的数据,key-value对', \n" +
"\t`template_merge_data` varchar(512) /*!50616 COLUMN_FORMAT COMPRESSED */ DEFAULT NULL COMMENT '对于需要合并的消息,被合并的参数放在此处理', \n" +
"\t`expiration_date` datetime NOT NULL COMMENT '失效日期,精度到天,查询和任务处理时使用', \n" +
"\t`merge_key` varchar(128) DEFAULT NULL COMMENT '消息合并主键,合并成功的消息,不参与计数', \n" +
"\t`out_id` varchar(256) /*!50616 COLUMN_FORMAT COMPRESSED */ DEFAULT NULL COMMENT '外部关联源的id', \n" +
"\t`number_expiration_date` datetime DEFAULT NULL COMMENT '计数过期时间', \n" +
"\t`hidden` int(11) DEFAULT '0' COMMENT '标记是否在吊顶展示,1表示隐藏,0表示展示', \n" +
"\t`popup` tinyint(3) UNSIGNED DEFAULT '0' COMMENT '表示消息提醒方式:0-数字提醒,1-layer,2-popup', \n" +
"\t`tag_id` bigint(20) UNSIGNED DEFAULT NULL COMMENT 'TAG标志', \n" +
"\t`attribute` varchar(512) /*!50616 COLUMN_FORMAT COMPRESSED */ DEFAULT NULL COMMENT '消息的一些属性', \n" +
"\t`original_msg_ids` varchar(1024) /*!50616 COLUMN_FORMAT COMPRESSED */ DEFAULT NULL COMMENT '原始消息ID,多个id用半角逗号隔开', \n" +
"\tPRIMARY KEY (`id`), \n" +
"\tKEY `idx_expiration_date` (`expiration_date`), \n" +
"\tKEY `k_rid_hidd_cid` (`receiver_id`, `hidden`, `cat_id`, `expiration_date`), \n" +
"\tKEY `k_rid_aid_tid` (`receiver_id`, `app_id`, `type_id`, `expiration_date`), \n" +
"\tKEY `k_rid_stat_popup` (`receiver_id`, `status`, `popup`, `expiration_date`), \n" +
"\tKEY `k_tid` (`tag_id`)\n" +
") ENGINE = InnoDB AUTO_INCREMENT = 167279613030 CHARSET = gbk COMMENT = '消息实体表'", output);
}
{
String output = SQLUtils.toMySqlString(stmt, SQLUtils.DEFAULT_LCASE_FORMAT_OPTION);
Assert.assertEquals("create table `log_info_20170516` (\n" +
"\t`toid` varchar(40) character set utf8 not null, \n" +
"\t`title` varchar(40) character set utf8 default null, \n" +
"\t`traceid` varchar(50) character set utf8 default null, \n" +
"\t`parentid` varchar(40) character set utf8 default null, \n" +
"\t`key1` varchar(100) character set utf8 default null, \n" +
"\t`key2` varchar(100) character set utf8 default null, \n" +
"\t`key3` varchar(100) character set utf8 default null, \n" +
"\t`type` varchar(50) character set utf8 default null, \n" +
"\t`createdate` varchar(30) character set utf8 default null, \n" +
"\t`classmethod` varchar(100) character set utf8 default null, \n" +
"\t`linenum` int(11) default null, \n" +
"\t`threadname` varchar(80) character set utf8 default '', \n" +
"\t`content` text character set utf8, \n" +
"\tprimary key (`toid`), \n" +
"\tkey `traceid_index` (`traceid`)\n" +
") engine = InnoDB charset = utf8mb4", output);
Assert.assertEquals("create table `sys_msg_entry_0320` (\n" +
"\t`provider_dsp_name` varchar(256) /*!50616 COLUMN_FORMAT COMPRESSED */ default null comment '消息提供者的显示名,提供给一些需要统一发送名称的系统消息', \n" +
"\t`template_data` varchar(2048) not null /*!50616 COLUMN_FORMAT COMPRESSED */ comment '模板渲染时使用的数据,key-value对', \n" +
"\t`template_merge_data` varchar(512) /*!50616 COLUMN_FORMAT COMPRESSED */ default null comment '对于需要合并的消息,被合并的参数放在此处理', \n" +
"\t`expiration_date` datetime not null comment '失效日期,精度到天,查询和任务处理时使用', \n" +
"\t`merge_key` varchar(128) default null comment '消息合并主键,合并成功的消息,不参与计数', \n" +
"\t`out_id` varchar(256) /*!50616 COLUMN_FORMAT COMPRESSED */ default null comment '外部关联源的id', \n" +
"\t`number_expiration_date` datetime default null comment '计数过期时间', \n" +
"\t`hidden` int(11) default '0' comment '标记是否在吊顶展示,1表示隐藏,0表示展示', \n" +
"\t`popup` tinyint(3) unsigned default '0' comment '表示消息提醒方式:0-数字提醒,1-layer,2-popup', \n" +
"\t`tag_id` bigint(20) unsigned default null comment 'TAG标志', \n" +
"\t`attribute` varchar(512) /*!50616 COLUMN_FORMAT COMPRESSED */ default null comment '消息的一些属性', \n" +
"\t`original_msg_ids` varchar(1024) /*!50616 COLUMN_FORMAT COMPRESSED */ default null comment '原始消息ID,多个id用半角逗号隔开', \n" +
"\tprimary key (`id`), \n" +
"\tkey `idx_expiration_date` (`expiration_date`), \n" +
"\tkey `k_rid_hidd_cid` (`receiver_id`, `hidden`, `cat_id`, `expiration_date`), \n" +
"\tkey `k_rid_aid_tid` (`receiver_id`, `app_id`, `type_id`, `expiration_date`), \n" +
"\tkey `k_rid_stat_popup` (`receiver_id`, `status`, `popup`, `expiration_date`), \n" +
"\tkey `k_tid` (`tag_id`)\n" +
") engine = InnoDB auto_increment = 167279613030 charset = gbk comment = '消息实体表'", output);
}
}
}
@@ -0,0 +1,66 @@
/*
* Copyright 1999-2017 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.select;
import com.alibaba.druid.sql.MysqlTest;
import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.ast.statement.SQLSelect;
import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock;
import com.alibaba.druid.sql.ast.statement.SQLSelectStatement;
import com.alibaba.druid.sql.ast.statement.SQLTableSource;
import com.alibaba.druid.sql.visitor.SchemaStatVisitor;
import com.alibaba.druid.util.JdbcConstants;
import org.junit.Assert;
import java.util.List;
public class MySqlSelectTest_comma_src extends MysqlTest {
public void test_0() throws Exception {
String sql = "select * from t1, t2, t3";
List<SQLStatement> statementList = SQLUtils.parseStatements(sql, JdbcConstants.MYSQL, true);
Assert.assertEquals(1, statementList.size());
SQLStatement stmt = statementList.get(0);
SQLSelect select = ((SQLSelectStatement)stmt).getSelect();
SQLTableSource tableSource = ((SQLSelectQueryBlock)select.getQuery()).getFrom();
SchemaStatVisitor visitor = SQLUtils.createSchemaStatVisitor(JdbcConstants.MYSQL);
stmt.accept(visitor);
// System.out.println("Tables : " + visitor.getTables());
// System.out.println("fields : " + visitor.getColumns());
// System.out.println("coditions : " + visitor.getConditions());
// System.out.println("orderBy : " + visitor.getOrderByColumns());
// Assert.assertEquals(1, visitor.getTables().size());
// Assert.assertEquals(1, visitor.getColumns().size());
// Assert.assertEquals(0, visitor.getConditions().size());
// Assert.assertEquals(0, visitor.getOrderByColumns().size());
{
String output = SQLUtils.toMySqlString(stmt);
Assert.assertEquals("SELECT *\n" +
"FROM t1, t2, t3", //
output);
}
}
}
@@ -52,10 +52,11 @@ public class OracleAlterTableTest19 extends OracleTest {
System.out.println("relationships : " + visitor.getRelationships());
System.out.println("orderBy : " + visitor.getOrderByColumns());
Assert.assertEquals("ALTER TABLE warehouses" //
+ "\n\tADD CONSTRAINT wh_unq UNIQUE (warehouse_id, warehouse_name)"//
+ "\n\tUSING INDEX PCTFREE 5"//
+ "\n\tEXCEPTIONS INTO wrong_id", SQLUtils.toSQLString(stmt, JdbcConstants.ORACLE));
Assert.assertEquals("ALTER TABLE warehouses\n" +
"\tADD CONSTRAINT wh_unq UNIQUE (warehouse_id, warehouse_name)\n" +
"\t\tUSING INDEX\n" +
"\t\tPCTFREE 5\n" +
"\t\tEXCEPTIONS INTO wrong_id", SQLUtils.toSQLString(stmt, JdbcConstants.ORACLE));
Assert.assertEquals(1, visitor.getTables().size());
@@ -61,7 +61,7 @@ public class OracleBlockTest10 extends OracleTest {
Assert.assertTrue(visitor.getTables().containsKey(new TableStat.Name("employees")));
Assert.assertTrue(visitor.getTables().containsKey(new TableStat.Name("departments")));
Assert.assertEquals(5, visitor.getColumns().size());
Assert.assertEquals(6, visitor.getColumns().size());
Assert.assertEquals(3, visitor.getConditions().size());
// Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("employees", "salary")));
@@ -52,21 +52,18 @@ public class OracleCreateTableTest18 extends OracleTest {
Assert.assertEquals(1, statementList.size());
Assert.assertEquals("CREATE TABLE \"SONAR\".\"AUTHORS\" (" //
+ "\n\t\"ID\" NUMBER(38, 0) NOT NULL ENABLE," //
+ "\n\t\"PERSON_ID\" NUMBER(38, 0) NOT NULL ENABLE," //
+ "\n\t\"LOGIN\" VARCHAR2(100 BYTE)," //
+ "\n\t\"CREATED_AT\" TIMESTAMP(6)," //
+ "\n\t\"UPDATED_AT\" TIMESTAMP(6)," //
+ "\n\tPRIMARY KEY (\"ID\")"
+ "\n\tUSING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS TABLESPACE \"USERS\" ENABLE" //
+ "\n)"
+ "\nPCTFREE 10"
+ "\nINITRANS 1"
+ "\nMAXTRANS 255"
+ "\nNOCOMPRESS"
+ "\nLOGGING"
+ "\nTABLESPACE \"USERS\"", //
Assert.assertEquals("CREATE TABLE dept_20 (\n" +
"\temployee_id NUMBER(4),\n" +
"\tlast_name VARCHAR2(10),\n" +
"\tjob_id VARCHAR2(9),\n" +
"\tmanager_id NUMBER(4),\n" +
"\thire_date DATE,\n" +
"\tsalary NUMBER(7, 2),\n" +
"\tcommission_pct NUMBER(7, 2),\n" +
"\tdepartment_id,\n" +
"\tCONSTRAINT fk_deptno FOREIGN KEY (department_id)\n" +
"\t\tREFERENCES departments (department_id)\n" +
")", //
SQLUtils.toSQLString(statement, JdbcConstants.ORACLE));
OracleSchemaStatVisitor visitor = new OracleSchemaStatVisitor();
@@ -77,7 +77,6 @@ import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleMultiInsertStatement.
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OraclePLSQLCommitStatement;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OraclePrimaryKey;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSavePointStatement;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectForUpdate;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectPivot;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectQueryBlock;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectRestriction.CheckOption;
@@ -100,7 +99,6 @@ public class OracleASTVisitorAdapterTest extends TestCase {
new OracleAnalyticWindowing().accept(adapter);
new SQLDateExpr().accept(adapter);
new OracleDbLinkExpr().accept(adapter);
new OracleSelectForUpdate().accept(adapter);
new OracleSelectPivot.Item().accept(adapter);
new OracleSelectPivot().accept(adapter);
new CheckOption().accept(adapter);