init from mycat1.6

This commit is contained in:
yanhuqing666
2016-11-16 15:36:35 +08:00
commit f4b708904d
1076 changed files with 141154 additions and 0 deletions
+113
View File
@@ -0,0 +1,113 @@
### Eclipse template
*.pydevproject
.metadata
.gradle
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.settings/
.loadpath
# Eclipse Core
.project
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# CDT-specific
.cproject
# JDT-specific (Eclipse Java Development Tools)
.classpath
# Java annotation processor (APT)
.factorypath
# PDT-specific
.buildpath
# sbteclipse plugin
.target
# TeXlipse plugin
.texlipse
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion
*.iml
## Directory-based project format:
.idea/
# if you remove the above rule, at least ignore the following:
# User-specific stuff:
# .idea/workspace.xml
# .idea/tasks.xml
# .idea/dictionaries
# Sensitive or high-churn files:
# .idea/dataSources.ids
# .idea/dataSources.xml
# .idea/sqlDataSources.xml
# .idea/dynamic.xml
# .idea/uiDesigner.xml
# Gradle:
# .idea/gradle.xml
# .idea/libraries
# Mongo Explorer plugin:
# .idea/mongoSettings.xml
## File-based project format:
*.ipr
*.iws
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
### Java template
*.class
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.ear
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
/project/project/
/project/target/
/project/activator-*
/logs/
/RUNNING_PID
.DS_Store
/target/
+4
View File
@@ -0,0 +1,4 @@
language: java
jdk:
- openjdk7
- oraclejdk7
+339
View File
@@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/>
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
{description}
Copyright (C) {year} {fullname}
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
{signature of Ty Coon}, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
+425
View File
@@ -0,0 +1,425 @@
# [MyCAT](http://mycat.io/)
[![GitHub issues](https://img.shields.io/github/issues/MyCATApache/Mycat-Server.svg)](https://github.com/MyCATApache/Mycat-Server/issues)
[![GitHub forks](https://img.shields.io/github/forks/MyCATApache/Mycat-Server.svg)](https://github.com/MyCATApache/Mycat-Server/network)
[![GitHub stars](https://img.shields.io/github/stars/MyCATApache/Mycat-Server.svg)](https://github.com/MyCATApache/Mycat-Server/stargazers)
[![MyCAT](https://img.shields.io/badge/MyCAT-%E2%9D%A4%EF%B8%8F-%23ff69b4.svg)](http://mycat.io/)
MyCAT is an Open-Source software, “a large database cluster” oriented to enterprises. MyCAT is an enforced database which is a replacement for MySQL and supports transaction and ACID. Regarded as MySQL cluster of enterprise database, MyCAT can take the place of expensive Oracle cluster. MyCAT is also a new type of database, which seems like a SQL Server integrated with the memory cache technology, NoSQL technology and HDFS big data. And as a new modern enterprise database product, MyCAT is combined with the traditional database and new distributed data warehouse. In a word, MyCAT is a fresh new middleware of database.
Mycats target is to smoothly migrate the current stand-alone database and applications to cloud side with low cost and to solve the bottleneck problem caused by the rapid growth of data storage and business scale.
* [Getting Started](https://github.com/MyCATApache/Mycat-doc/tree/master/en)
* [尝试 MyCAT](https://github.com/MyCATApache/Mycat-doc/blob/master/MyCat_In_Action_%E4%B8%AD%E6%96%87%E7%89%88.doc)
## Features
* Supports SQL 92 standard
* Supports MySQL cluster, used as a Proxy
* Supports JDBC connection with ORACLE, DB2, SQL Server, simulated as normal MySQL Server connection
* Supports MySQL cluster, percona cluster or mariadb cluster, providing high availability of data fragmentation clusters
* Supports automatic failover and high availability
* Supports separation of read and write, dual-master with multi-slave, single-master with multi-master of MySQL model
* Supports global table, automatically fragment data into multiple nodes for efficient relational query
* Supports the unique fragmentation strategy based on ER-relation for efficient relational query
* Supports multiple platforms, easy deployment and implementation
## Advantage
* Based on Alibaba's open-source project [Cobar](https://github.com/alibaba/cobar), whose stability, reliability, excellent architecture and performance, as well as many mature use-cases make MyCAT have a good starting. Standing on the shoulders of giants, MyCAT feels confident enough to go farther.
* Extensively drawing on the best open-source projects and innovative ideas, which are integrated into the Mycats gene, make MyCAT be ahead of the other current similar open-source projects, even beyond some commercial products.
* MyCAT behind a strong technical team whose participants are experienced more than five years including some senior software engineer, architect, DBA, etc. Excellent technical team to ensure the product quality of Mycat.
* MyCAT does not rely on any commercial company. Its unlike some open-source projects whose important features is enclosed in its commercial products and making open-source projects like a decoration.
## Roadmap
* On the basis of MySQLs support, MyCAT add more support of commercial open-source database, including native support of PostgreSQL, FireBird and other open-source databases, as well as indirect support via JDBC of other non-open-source databases such as Oracle, DB2, SQL Server etc.
* More intelligent self-regulating properties, such as automatic statistical analysis of SQL, automatic creating and adjusting indexes. Based on the frequency of read and write, MyCAT automatically optimizes caching and backup strategies
* Achieve a more comprehensive monitoring and management
* Integrated with HDFS, provide SQL commands, load databases into HDFS for rapid analysis
* Integrated excellent open-source reporting tools to make MyCAT have data analysis capability
## Download
There are some compiled binary installation packages in Mycat-download project on github at [Mycat-download](https://github.com/MyCATApache/Mycat-download).
## Document
There are some documents in Mycat-doc project on github at [Mycat-doc](https://github.com/MyCATApache/Mycat-doc).
Mycat 简单demo,具体参考Mycat权威指南
官网 : mycat.io
qq官方群:106088787
Mycat权威指南官方下载:http://songwie.com/attached/file/mycat_1.5.2.pdf
wiki<a href="https://github.com/MyCATApache/Mycat-Server/wiki"> wiki</a>
# Mycat前世今生
2013年阿里的Cobar在社区使用过程中发现存在一些比较严重的问题,及其使用限制,经过Mycat发起人第一次改良,第一代改良版——Mycat诞生。 Mycat开源以后,一些Cobar的用户参与了Mycat的开发,最终Mycat发展成为一个由众多软件公司的实力派架构师和资深开发人员维护的社区型开源软件。
2014年Mycat首次在上海的《中华架构师》大会上对外宣讲,更多的人参与进来,随后越来越多的项目采用了Mycat。
2015年5月,由核心参与者们一起编写的第一本官方权威指南《Mycat权威指南》电子版发布,累计超过500本,成为开源项目中的首创。
2015年10月为止,Mycat项目总共有16个Committer。
截至2015年11月,超过300个项目采用Mycat,涵盖银行、电信、电子商务、物流、移动应用、O2O的众多领域和公司。
截至2015年12月,超过4000名用户加群或研究讨论或测试或使用Mycat。
Mycat是基于开源cobar演变而来,我们对cobar的代码进行了彻底的重构,使用NIO重构了网络模块,并且优化了Buffer内核,增强了聚合,Join等基本特性,同时兼容绝大多数数据库成为通用的数据库中间件。1.4 版本以后 完全的脱离基本cobar内核,结合Mycat集群管理、自动扩容、智能优化,成为高性能的中间件。我们致力于开发高性能数据库中间而努力。永不收费,永不闭源,持续推动开源社区的发展。
Mycat吸引和聚集了一大批业内大数据和云计算方面的资深工程师,Mycat的发展壮大基于开源社区志愿者的持续努力,感谢社区志愿者的努力让Mycat更加强大,同时我们也欢迎社区更多的志愿者,特别是公司能够参与进来,参与Mycat的开发,一起推动社区的发展,为社区提供更好的开源中间件。
Mycat还不够强大,Mycat还有很多不足,欢迎社区志愿者的持续优化改进。
# 关键特性
支持SQL92标准
遵守Mysql原生协议,跨语言,跨平台,跨数据库的通用中间件代理。
基于心跳的自动故障切换,支持读写分离,支持MySQL主从,以及galera cluster集群。
支持Galera for MySQL集群,Percona Cluster或者MariaDB cluster
基于Nio实现,有效管理线程,高并发问题。
支持数据的多片自动路由与聚合,支持sum,count,max等常用的聚合函数。
支持单库内部任意join,支持跨库2表join,甚至基于caltlet的多表join。
支持通过全局表,ER关系的分片策略,实现了高效的多表join查询。
支持多租户方案。
支持分布式事务(弱xa)。
支持全局序列号,解决分布式下的主键生成问题。
分片规则丰富,插件化开发,易于扩展。
强大的web,命令行监控。
支持前端作为mysq通用代理,后端JDBC方式支持Oracle、DB2、SQL Server 、 mongodb 、巨杉。
支持密码加密
支持服务降级
支持IP白名单
支持SQL黑名单、sql注入攻击拦截
支持分表(1.6
集群基于ZooKeeper管理,在线升级,扩容,智能优化,大数据处理(2.0开发版)。
# Mycat安装与使用
## 下载:
[https://github.com/MyCATApache/Mycat-download](https://github.com/MyCATApache/Mycat-download)
具体下载哪个版本以发布为准,推荐1.4,1.5.
## 安装:
下载的文件直接解压即可。
## 运行:
### linux
./mycat start 启动
./mycat stop 停止
./mycat console 前台运行
./mycat install 添加到系统自动启动(暂未实现)
./mycat remove 取消随系统自动启动(暂未实现)
./mycat restart 重启服务
./mycat pause 暂停
./mycat status 查看启动状态
### win
直接运行startup_nowrap.bat,如果出现闪退,在cmd 命令行运行,查看出错原因。
## 内存配置:
启动前,一般需要修改JVM配置参数,打开conf/wrapper.conf文件,如下行的内容为2G和2048,可根据本机配置情况修改为512M或其它值。
以下配置跟jvm参数完全一致,可以根据自己的jvm参数调整。
Java Additional Parameters
wrapper.java.additional.1=
wrapper.java.additional.1=-DMYCAT_HOME=.
wrapper.java.additional.2=-server
wrapper.java.additional.3=-XX:MaxPermSize=64M
wrapper.java.additional.4=-XX:+AggressiveOpts
wrapper.java.additional.5=-XX:MaxDirectMemorySize=100m
wrapper.java.additional.6=-Dcom.sun.management.jmxremote
wrapper.java.additional.7=-Dcom.sun.management.jmxremote.port=1984
wrapper.java.additional.8=-Dcom.sun.management.jmxremote.authenticate=false
wrapper.java.additional.9=-Dcom.sun.management.jmxremote.ssl=false
wrapper.java.additional.10=-Xmx100m
wrapper.java.additional.11=-Xms100m
wrapper.java.additional.12=-XX:+UseParNewGC
wrapper.java.additional.13=-XX:+UseConcMarkSweepGC
wrapper.java.additional.14=-XX:+UseCMSCompactAtFullCollection
wrapper.java.additional.15=-XX:CMSFullGCsBeforeCompaction=0
wrapper.java.additional.16=-XX:CMSInitiatingOccupancyFraction=70
以下配置作废:
wrapper.java.initmemory=3
wrapper.java.maxmemory=64
### Mycat连接测试:
测试mycat与测试mysql完全一致,mysql怎么连接,mycat就怎么连接。
推荐先采用命令行测试:
mysql -uroot -proot -P8066 -h127.0.0.1
如果采用工具连接,1.4,1.3目前部分工具无法连接,会提示database not selected,建议采用高版本,navicat测试。1.5已经修复了部分工具连接。
# Mycat配置入门
## 配置:
--bin 启动目录
--conf 配置文件存放配置文件:
--server.xml:是Mycat服务器参数调整和用户授权的配置文件。
--schema.xml:是逻辑库定义和表以及分片定义的配置文件。
--rule.xml: 是分片规则的配置文件,分片规则的具体一些参数信息单独存放为文件,也在这个目录下,配置文件修改需要重启MyCAT。
--log4j.xml 日志存放在logs/log中,每天一个文件,日志的配置是在conf/log4j.xml中,根据自己的需要可以调整输出级别为debug debug级别下,会输出更多的信息,方便排查问题。
--autopartition-long.txt,partition-hash-int.txt,sequence_conf.properties sequence_db_conf.properties 分片相关的id分片规则配置文件
--lib MyCAT自身的jar包或依赖的jar包的存放目录。
--logs MyCAT日志的存放目录。日志存放在logs/log中,每天一个文件
下面图片描述了Mycat最重要的3大配置文件:
<p>
<img src="http://songwie.com/attached/image/20160205/20160205164558_154.png" alt="">
</p>
## 逻辑库配置:
### 配置server.xml
添加两个mycat逻辑库:user,pay:
system 参数是所有的mycat参数配置,比如添加解析器:defaultSqlParser,其他类推
user 是用户参数。
<system>
<property name="defaultSqlParser">druidparser</property>
</system>
<user name="mycat">
<property name="password">mycat</property>
<property name="schemas">user,pay</property>
</user>
### 编辑schema.xml
修改dataHost和schema对应的连接信息,user,pay 垂直切分后的配置如下所示:
schema 是实际逻辑库的配置,user,pay分别对应两个逻辑库,多个schema代表多个逻辑库。
dataNode是逻辑库对应的分片,如果配置多个分片只需要多个dataNode即可。
dataHost是实际的物理库配置地址,可以配置多主主从等其他配置,多个dataHost代表分片对应的物理库地址,下面的writeHost、readHost代表该分片是否配置多写,主从,读写分离等高级特性。
以下例子配置了两个writeHost为主从。
<schema name="user" checkSQLschema="false" sqlMaxLimit="100" dataNode="user" />
<schema name="pay" checkSQLschema="false" sqlMaxLimit="100" dataNode="pay" >
<table name="order" dataNode="pay1,pay2" rule="rule1"/>
</schema>
<dataNode name="user" dataHost="host" database="user" />
<dataNode name="pay1" dataHost="host" database="pay1" />
<dataNode name="pay2" dataHost="host" database="pay2" />
<dataHost name="host" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native">
<heartbeat>select 1</heartbeat>
<!-- can have multi write hosts -->
<writeHost host="hostM1" url="192.168.0.2:3306" user="root" password="root" />
<writeHost host="hostM2" url="192.168.0.3:3306" user="root" password="root" />
</dataHost>
# Mycat逻辑库、系统参数配置
## 配置Mycat环境参数
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE mycat:server SYSTEM "server.dtd">
<mycat:server xmlns:mycat="http://org.opencloudb/">
<system>
<property name="defaultSqlParser">druidparser</property>
</system>
</mycat:server>
如例子中配置的所有的Mycat参数变量都是配置在server.xml 文件中,system标签下配置所有的参数,如果需要配置某个变量添加相应的配置即可,例如添加启动端口8066,默认为8066:
<property name="serverPort">8066</property>
其他所有变量类似。
## 配置Mycat逻辑库与用户
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE mycat:server SYSTEM "server.dtd">
<mycat:server xmlns:mycat="http://org.opencloudb/">
<user name="mycat">
<property name="password">mycat</property>
<property name="schemas">TESTDB</property>
</user>
</mycat:server>
如例子中配置的所有的Mycat连接的用户与逻辑库映射都是配置在server.xml 文件中,user标签下配置所有的参数,例如例子中配置了一个mycat用户供应用连接到mycat,同时mycat 在schema.xml中配置后了一个逻辑库TESTDB,配置好逻辑库与用户的映射关系。
# 逻辑库、表分片配置
## 配置逻辑库(schema
Mycat作为一个中间件,实现mysql协议那么对前端应用连接来说就是一个数据库,也就有数据库的配置,mycat的数据库配置是在schema.xml中配置,配置好后映射到server.xml里面的用户就可以了。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://org.opencloudb/">
<schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" dataNode="dn1">
<table name="t_user" dataNode="dn1,dn2" rule="sharding-by-mod2"/>
<table name="ht_jy_login_log" primaryKey="ID" dataNode="dn1,dn2" rule="sharding-by-date_jylog"/>
</schema>
<dataNode name="dn1" dataHost="localhost1" database="mycat_node1"/>
<dataNode name="dn2" dataHost="localhost1" database="mycat_node2"/>
<dataHost name="localhost1" writeType="0" switchType="1" slaveThreshold="100" balance="1" dbType="mysql" maxCon="10" minCon="1" dbDriver="native">
<heartbeat>show status like 'wsrep%'</heartbeat>
<writeHost host="hostM1" url="127.0.0.1:3306" user="root" password="root" >
</writeHost>
</dataHost>
</mycat:schema >
上面例子配置了一个逻辑库TESTDB,同时配置了t_userht_jy_login_log两个分片表。
### 逻辑表配置
<table name="t_user" dataNode="dn1,dn2" rule="sharding-by-mod2"/>
table 标签 是逻辑表的配置 其中
name代表表名,
dataNode代表表对应的分片,
Mycat默认采用分库方式,也就是一个表映射到不同的库上,
rule代表表要采用的数据切分方式,名称对应到rule.xml中的对应配置,如果要分片必须配置。
## 配置分片(dataNode
<dataNode name="dn1" dataHost="localhost1" database="mycat_node1"/>
<dataNode name="dn2" dataHost="localhost1" database="mycat_node2"/>
表切分后需要配置映射到哪几个数据库中,Mycat的分片实际上就是库的别名,例如上面例子配置了两个分片dn1,dn2 分别对应到物理机映射dataHost
localhost1 的两个库上。
## 配置物理库分片映射(dataHost)
<dataHost name="localhost1" writeType="0" switchType="1" slaveThreshold="100" balance="1" dbType="mysql" maxCon="10" minCon="1" dbDriver="native">
<heartbeat>show status like 'wsrep%'</heartbeat>
<writeHost host="hostM1" url="127.0.0.1:3306" user="root" password="root" >
</writeHost>
</dataHost>
Mycat作为数据库代理需要逻辑库,逻辑用户,表切分后需要配置分片,分片也就需要映射到真实的物理主机上,至于是映射到一台还是一台的多个实例上,Mycat并不关心,只需要配置好映射即可,例如例子中:
配置了一个名为localhost1的物理主机(dataHost)映射。
heartbeat 标签代表Mycat需要对物理库心跳检测的语句,正常情况下生产案例可能配置主从,或者多写 或者单库,无论哪种情况Mycat都需要维持到数据库的数据源连接,因此需要定时检查后端连接可以性,心跳语句就是来作为心跳检测。
writeHost 此标签代表 一个逻辑主机(dataHost)对应的后端的物理主机映射,例如例子中写库hostM1 映射到127.0.0.1:3306。如果后端需要做读写分离或者多写 或者主从则通过配置 多个writeHost 或者readHost即可。
dataHost 标签中的 writeType balance 等标签则是不同的策略,具体参考指南。
# Mycat 表切分规则配置
## 表切分规则
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mycat:rule SYSTEM "rule.dtd">
<mycat:rule xmlns:mycat="http://org.opencloudb/">
<tableRule name="sharding-by-hour">
<rule>
<columns>createTime</columns>
<algorithm>sharding-by-hour</algorithm>
</rule>
</tableRule>
<function name="sharding-by-hour" class="org.opencloudb.route.function.LatestMonthPartion">
<property name="splitOneDay">24</property>
</function>
</mycat:rule >
数据切分中作为表切分规则中最重要的配置,表的切分方式决定了数据切分后的性能好坏,因此也是最重要的配置。
如上面例子配置了一个切分规则,名为sharding-by-hour 对应的切分方式(function )是按日期切分,该配置中:
### tableRule
name 为schema.xml 中table 标签中对应的 rule="sharding-by-hour" ,也就是配置表的分片规则,
columns 是表的切分字段: createTime 创建日期。
algorithm 是规则对应的切分规则:映射到function 的name。
### function
function 配置是分片规则的配置。
name 为切分规则的名称,名字人员取,但是需要与tableRule 中匹配。
class 是切分规则对应的切分类,写死,需要哪种规则则配置哪种,例如本例子是按小时分片:org.opencloudb.route.function.LatestMonthPartion
property 标签是切分规则对应的不同属性,不同的切分规则配置不同。
+59
View File
@@ -0,0 +1,59 @@
### Mycat介绍
### 官网:[http://www.mycat.org.cn](http://www.mycat.org.cn)
### github:[https://github.com/MyCATApache](https://github.com/MyCATApache)
##### 入门: [zh-CN: https://github.com/MyCATApache/Mycat-doc/blob/master/MyCat_In_Action_%E4%B8%AD%E6%96%87%E7%89%88.doc] [English:https://github.com/MyCATApache/Mycat-doc/tree/master/en]
什么是Mycat?简单的说,Mycat就是:
* 一个彻底开源的,面向企业应用开发的“大数据库集群”
* 支持事务、ACID、可以替代MySQL的加强版数据库
* 一个可以视为“MySQL”集群的企业级数据库,用来替代昂贵的Oracle集群
* 一个融合内存缓存技术、Nosql技术、HDFS大数据的新型SQL Server
* 结合传统数据库和新型分布式数据仓库的新一代企业级数据库产品
* 一个新颖的数据库中间件产品
##### Mycat的目标是:
低成本的将现有的单机数据库和应用平滑迁移到“云”端,解决数据存储和业务规模迅速增长情况下的数据瓶颈问题。
##### Mycat的关键特性:
* 支持 SQL 92标准
* 支持MySQL集群,可以作为Proxy使用
* 支持JDBC连接ORACLE、DB2、SQL Server,将其模拟为MySQL Server使用
* 支持galera for MySQL集群,percona-cluster或者mariadb cluster,提供高可用性数据分片集群
* 自动故障切换,高可用性
* 支持读写分离,支持MySQL双主多从,以及一主多从的模式
* 支持全局表,数据自动分片到多个节点,用于高效表关联查询
* 支持独有的基于E-R 关系的分片策略,实现了高效的表关联查询
* 多平台支持,部署和实施简单
##### Mycat的优势:
* 基于阿里开源的Cobar产品而研发,Cobar的稳定性、可靠性、优秀的架构和性能,以及众多成熟的使用案例使得Mycat一开始就拥有一个很好的起点,站在巨人的肩膀上,我们能看到更远。
* 广泛吸取业界优秀的开源项目和创新思路,将其融入到Mycat的基因中,使得Mycat在很多方面都领先于目前其他一些同类的开源项目,甚至超越某些商业产品。
* Mycat背后有一只强大的技术团队,其参与者都是5年以上资深软件工程师、架构师、DBA等,优秀的技术团队保证了Mycat的产品质量。
* Mycat并不依托于任何一个商业公司,因此不像某些开源项目,将一些重要的特性封闭在其商业产品中,使得开源项目成了一个摆设。
##### Mycat的长期路线规划:
* 在支持MySQL的基础上,后端增加更多的开源数据库和商业数据库的支持,包括原生支持PosteSQL、FireBird等开源数据库,以及通过JDBC等方式间接支持其他非开源的数据库如Oracle、DB2、SQL Server等
* 实现更为智能的自我调节特性,如自动统计分析SQL,自动创建和调整索引,根据数据表的读写频率,自动优化缓存和备份策略等
* 实现更全面的监控管理功能
* 与HDFS集成,提供SQL命令,将数据库装入HDFS中并能够快速分析
* 集成优秀的开源报表工具,使之具备一定的数据分析的能力
##### 下载:
github上面的Mycat-download项目是编译好的二进制安装包 [https://github.com/MyCATApache/Mycat-download](https://github.com/MyCATApache/Mycat-download)
##### 文档:
github上面的Mycat-doc项目是相关文档 [https://github.com/MyCATApache/Mycat-doc](https://github.com/MyCATApache/Mycat-doc)
+1
View File
@@ -0,0 +1 @@
put your customer Catlet class files in this dir
+7
View File
@@ -0,0 +1,7 @@
#update
#Mon Oct 19 16:26:50 CST 2015
dataHost2=0
localhost1=0
dataHost1=0
jdbclhost=0
jdbchost=0
+602
View File
@@ -0,0 +1,602 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>io.mycat</groupId>
<artifactId>Mycat-server</artifactId>
<version>1.6.5-DEV</version>
<packaging>jar</packaging>
<name>Mycat-server</name>
<description>The project of Mycat-server</description>
<url>http://io.mycat</url>
<properties>
<app.encoding>UTF-8</app.encoding>
<!-- maven.build.timestamp.format>yyyyMMdd</maven.build.timestamp.format>
<buildNumber>${maven.build.timestamp}</buildNumber -->
<maven.build.timestamp.format>yyyy-MM-dd HH:mm:ss</maven.build.timestamp.format>
<version.template.file>version.txt.template</version.template.file>
<version.file>version.txt</version.file>
</properties>
<scm>
<connection>scm:git:ssh://apachemycat@github.com/MyCATApache/Mycat-Server.git</connection>
<developerConnection>scm:git:ssh://apachemycat@github.com/MyCATApache/Mycat-Server.git</developerConnection>
<url>scm:git:ssh://apachemycat@github.com/MyCATApache/Mycat-Server.git</url>
</scm>
<repositories>
<repository>
<id>nexus</id>
<name>local private nexus</name>
<url>http://nexus.mycat.io/content/groups/public</url>
</repository>
</repositories>
<distributionManagement>
<repository>
<id>releases</id>
<name>Internal Releases</name>
<url>http://nexus.mycat.io/content/repositories/releases</url>
</repository>
<snapshotRepository>
<id>snapshots</id>
<name>Internal Snapshots</name>
<url>http://nexus.mycat.io/content/repositories/snapshots</url>
</snapshotRepository>
</distributionManagement>
<dependencies>
<!-- <dependency> <groupId>com.google.guava</groupId> <artifactId>guava-parent</artifactId>
<version>18.0</version> </dependency> -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>2.11.4</version>
</dependency>
<dependency>
<groupId>org.iq80.leveldb</groupId>
<artifactId>leveldb</artifactId>
<version>0.7</version>
</dependency>
<dependency>
<groupId>org.iq80.leveldb</groupId>
<artifactId>leveldb-api</artifactId>
<version>0.7</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>19.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.26</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.35</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>2.6.11</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.mapdb</groupId>
<artifactId>mapdb</artifactId>
<version>1.0.7</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
<version>1.7</version>
</dependency>
<dependency>
<groupId>org.codehaus.jsr166-mirror</groupId>
<artifactId>jsr166y</artifactId>
<version>1.7.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.3.4</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-1.2-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>com.univocity</groupId>
<artifactId>univocity-parsers</artifactId>
<version>2.2.1</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>com.sequoiadb</groupId>
<artifactId>sequoiadb-driver</artifactId>
<version>1.12</version>
</dependency>
<!--DOM4J FOR XML -->
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
<exclusions>
<exclusion>
<groupId>xml-apis</groupId>
<artifactId>xml-apis</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- zookeeper -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-test</artifactId>
<version>2.11.0</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.12</version>
</dependency>
<!-- joda日期处理工具 -->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.9.3</version>
</dependency>
<dependency>
<groupId>com.github.shyiko</groupId>
<artifactId>mysql-binlog-connector-java</artifactId>
<version>0.4.1</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.8.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<version>2.0.3</version>
</dependency>
<dependency>
<groupId>com.esotericsoftware.kryo</groupId>
<artifactId>kryo</artifactId>
<version>2.10</version>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<version>1.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-lang/commons-lang -->
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
</dependencies>
<issueManagement>
<system>JIRA</system>
<url>http://io.mycat</url>
</issueManagement>
<build>
<!-- finalName>${artifactId}-${version}-${buildNumber}</finalName -->
<resources>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>**/.svn/**</exclude>
</excludes>
</resource>
<resource>
<directory>${basedir}</directory>
<includes>
<include>${version.file}</include>
</includes>
</resource>
</resources>
<testResources>
<testResource>
<directory>src/test/resources</directory>
<excludes>
<exclude>**/.svn/**</exclude>
</excludes>
</testResource>
</testResources>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>com.google.code.maven-replacer-plugin</groupId>
<artifactId>replacer</artifactId>
<versionRange>[1.0.0,)</versionRange>
<goals>
<goal>replace</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore />
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>com.google.code.maven-replacer-plugin</groupId>
<artifactId>replacer</artifactId>
<version>1.5.3</version>
<executions>
<execution>
<id>version</id>
<phase>process-sources</phase>
<goals>
<goal>replace</goal>
</goals>
<configuration>
<file>${project.basedir}/${version.template.file}</file>
<outputFile>${project.basedir}/${version.file}</outputFile>
<replacements>
<replacement>
<token>@buildnumber@</token>
<value>${buildNumber}</value>
</replacement>
<replacement>
<token>@buildtime@</token>
<value>${maven.build.timestamp}</value>
</replacement>
<replacement>
<token>@pomversion@</token>
<value>${project.version}</value>
</replacement>
<replacement>
<token>@giturl@</token>
<value>https://github.com/MyCATApache/Mycat-Server.git</value>
</replacement>
<replacement>
<token>@mycatsite@</token>
<value>http://www.mycat.org.cn</value>
</replacement>
<replacement>
<token>@qqgroup@</token>
<value>106088787</value>
</replacement>
</replacements>
</configuration>
</execution>
<execution>
<id>version2</id>
<phase>process-sources</phase>
<goals>
<goal>replace</goal>
</goals>
<configuration>
<file>${project.basedir}/src/main/java/io/mycat/config/Versions.template</file>
<outputFile>${project.basedir}/src/main/java/io/mycat/config/Versions.java</outputFile>
<replacements>
<replacement>
<token>@server-version@</token>
<value>5.6.29-mycat-${project.version}-${timestamp}</value>
</replacement>
</replacements>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
<encoding>${app.encoding}</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.1.2</version>
<configuration>
<encoding>${app.encoding}</encoding>
<attach>true</attach>
</configuration>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<excludes>
<exclude>**/.svn/**</exclude>
</excludes>
</configuration>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
<!-- configuration> <finalName>${project.build.finalName}-${buildNumber}</finalName>
</configuration -->
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<configuration>
<sourceExcludes>
<sourceExclude>**/.svn/**</sourceExclude>
</sourceExcludes>
<downloadSources>true</downloadSources>
<outputDirectory>classes</outputDirectory>
<additionalConfig>
<file>
<name>.settings/org.eclipse.core.resources.prefs</name>
<content>
<![CDATA[eclipse.preferences.version=1${line.separator}encoding/<project>=${app.encoding}${line.separator}]]>
</content>
</file>
</additionalConfig>
</configuration>
</plugin>
<!-- -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>appassembler-maven-plugin</artifactId>
<version>1.7</version>
<configuration>
<configurationDirectory>conf</configurationDirectory>
<includeConfigurationDirectoryInClasspath>true</includeConfigurationDirectoryInClasspath>
<repositoryLayout>flat</repositoryLayout>
<useWildcardClassPath>true</useWildcardClassPath>
<daemons>
<daemon>
<id>mycat</id>
<mainClass>io.mycat.MycatStartup</mainClass>
<commandLineArguments>
<commandLineArgument>start</commandLineArgument>
</commandLineArguments>
<platforms>
<platform>jsw</platform>
</platforms>
<jvmSettings>
<!-- 启动内存配置 -->
<maxStackSize>128</maxStackSize>
<systemProperties>
<systemProperty>MYCAT_HOME=.</systemProperty>
</systemProperties>
<extraArguments>
<extraArgument>-server </extraArgument>
<extraArgument>-XX:MaxPermSize=64M</extraArgument>
<extraArgument>-XX:+AggressiveOpts</extraArgument>
<extraArgument>-XX:MaxDirectMemorySize=2G</extraArgument>
<!-- 远程JMX -->
<extraArgument>-Dcom.sun.management.jmxremote </extraArgument>
<extraArgument>-Dcom.sun.management.jmxremote.port=1984</extraArgument>
<extraArgument>-Dcom.sun.management.jmxremote.authenticate=false </extraArgument>
<extraArgument>-Dcom.sun.management.jmxremote.ssl=false </extraArgument>
<extraArgument>-Xmx4G</extraArgument>
<extraArgument>-Xms1G</extraArgument>
</extraArguments>
</jvmSettings>
<generatorConfigurations>
<generatorConfiguration>
<generator>jsw</generator>
<includes>
<include>aix-ppc-32</include>
<include>aix-ppc-64</include>
<include>hpux-parisc-64</include>
<include>linux-x86-32</include>
<include>linux-x86-64</include>
<include>linux-ppc-64</include>
<include>macosx-ppc-32</include>
<include>macosx-x86-universal-32</include>
<include>macosx-universal-32</include>
<include>macosx-universal-64</include>
<include>solaris-sparc-32</include>
<include>solaris-sparc-64</include>
<include>solaris-x86-32</include>
<include>windows-x86-32</include>
<include>windows-x86-64</include>
</includes>
<configuration>
<property>
<name>configuration.directory.in.classpath.first</name>
<value>conf</value>
</property>
<property>
<name>wrapper.ping.timeout</name>
<value>120</value>
</property>
<property>
<name>set.default.REPO_DIR</name>
<value>lib</value>
</property>
<property>
<name>wrapper.logfile</name>
<value>logs/wrapper.log</value>
</property>
</configuration>
</generatorConfiguration>
</generatorConfigurations>
</daemon>
</daemons>
</configuration>
<executions>
<execution>
<id>generate-jsw</id>
<phase>package</phase>
<goals>
<goal>generate-daemons</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptors>
<descriptor>src/main/assembly/assembly-win.xml</descriptor>
<descriptor>src/main/assembly/assembly-linux.xml</descriptor>
<descriptor>src/main/assembly/assembly-mac.xml</descriptor>
<descriptor>src/main/assembly/assembly-solaris.xml</descriptor>
<descriptor>src/main/assembly/assembly-unix.xml</descriptor>
<descriptor>src/main/assembly/assembly-testtool.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-scm-plugin</artifactId>
<version>1.9.2</version>
<configuration>
<providerImplementations>
<git>jgit</git>
</providerImplementations>
</configuration>
<dependencies>
<dependency>
<groupId>org.apache.maven.scm</groupId>
<artifactId>maven-scm-provider-jgit</artifactId>
<version>1.9.2</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>buildnumber-maven-plugin</artifactId>
<version>1.3</version>
<executions>
<execution>
<phase>validate</phase>
<goals>
<goal>create</goal>
</goals>
</execution>
</executions>
<configuration>
<format>{1}</format>
<items>
<item>timestamp</item>
<item>scmVersion</item>
</items>
<doCheck>false</doCheck>
<doUpdate>false</doUpdate>
<timestampFormat>{0,date,yyyyMMddHHmmss}</timestampFormat>
<providerImplementations>
<git>git</git>
</providerImplementations>
</configuration>
<dependencies>
<dependency>
<groupId>org.apache.maven.scm</groupId>
<artifactId>maven-scm-provider-gitexe</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>org.tmatesoft.svnkit</groupId>
<artifactId>svnkit</artifactId>
<version>RELEASE</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>
+78
View File
@@ -0,0 +1,78 @@
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
<id>${timestamp}-linux</id>
<includeBaseDirectory>false</includeBaseDirectory>
<formats>
<format>tar.gz</format>
</formats>
<fileSets>
<fileSet>
<directory>target/generated-resources/appassembler/jsw/mycat/bin</directory>
<outputDirectory>mycat/bin</outputDirectory>
<fileMode>0755</fileMode>
<includes>
<include>mycat</include>
<include>wrapper-linux*</include>
</includes>
</fileSet>
<fileSet>
<directory>target/generated-resources/appassembler/jsw/mycat/lib</directory>
<outputDirectory>mycat/lib</outputDirectory>
<includes>
<include>*.jar</include>
<include>libwrapper-linux*</include>
</includes>
</fileSet>
<fileSet>
<directory>target/generated-resources/appassembler/jsw/mycat/conf</directory>
<outputDirectory>mycat/conf</outputDirectory>
<includes>
<include>*</include>
</includes>
</fileSet>
<fileSet>
<directory>src/main/resources</directory>
<outputDirectory>mycat/conf</outputDirectory>
<excludes>
<exclude>*.dtd</exclude>
<exclude>log4j*</exclude>
</excludes>
</fileSet>
<fileSet>
<directory>${basedir}</directory>
<outputDirectory>mycat/</outputDirectory>
<includes>
<include>version.txt</include>
</includes>
</fileSet>
<fileSet>
<directory>src/main/assembly/conf</directory>
<outputDirectory>mycat/conf</outputDirectory>
</fileSet>
<fileSet>
<directory>src/main/assembly/bin</directory>
<outputDirectory>mycat/bin</outputDirectory>
<fileMode>0755</fileMode>
<includes>
<include>*.sh</include>
</includes>
</fileSet>
<fileSet>
<directory>${basedir}/logs</directory>
<outputDirectory>mycat/logs</outputDirectory>
<excludes>
<exclude>**/*</exclude>
</excludes>
</fileSet>
<fileSet>
<directory>${basedir}/catlet</directory>
<outputDirectory>mycat/catlet</outputDirectory>
<excludes>
<exclude>**/*</exclude>
</excludes>
</fileSet>
</fileSets>
</assembly>
+78
View File
@@ -0,0 +1,78 @@
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
<id>${timestamp}-mac</id>
<includeBaseDirectory>false</includeBaseDirectory>
<formats>
<format>tar.gz</format>
</formats>
<fileSets>
<fileSet>
<directory>target/generated-resources/appassembler/jsw/mycat/bin</directory>
<outputDirectory>mycat/bin</outputDirectory>
<fileMode>0755</fileMode>
<includes>
<include>mycat</include>
<include>wrapper-macosx*</include>
</includes>
</fileSet>
<fileSet>
<directory>target/generated-resources/appassembler/jsw/mycat/lib</directory>
<outputDirectory>mycat/lib</outputDirectory>
<includes>
<include>*.jar</include>
<include>libwrapper-macosx*</include>
</includes>
</fileSet>
<fileSet>
<directory>target/generated-resources/appassembler/jsw/mycat/conf</directory>
<outputDirectory>mycat/conf</outputDirectory>
<includes>
<include>*</include>
</includes>
</fileSet>
<fileSet>
<directory>src/main/resources</directory>
<outputDirectory>mycat/conf</outputDirectory>
<excludes>
<exclude>*.dtd</exclude>
<exclude>log4j*</exclude>
</excludes>
</fileSet>
<fileSet>
<directory>src/main/assembly/conf</directory>
<outputDirectory>mycat/conf</outputDirectory>
</fileSet>
<fileSet>
<directory>${basedir}</directory>
<outputDirectory>mycat/</outputDirectory>
<includes>
<include>version.txt</include>
</includes>
</fileSet>
<fileSet>
<directory>src/main/assembly/bin</directory>
<outputDirectory>mycat/bin</outputDirectory>
<fileMode>0755</fileMode>
<includes>
<include>*.sh</include>
</includes>
</fileSet>
<fileSet>
<directory>${basedir}/logs</directory>
<outputDirectory>mycat/logs</outputDirectory>
<excludes>
<exclude>**/*</exclude>
</excludes>
</fileSet>
<fileSet>
<directory>${basedir}/catlet</directory>
<outputDirectory>mycat/catlet</outputDirectory>
<excludes>
<exclude>**/*</exclude>
</excludes>
</fileSet>
</fileSets>
</assembly>
+78
View File
@@ -0,0 +1,78 @@
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
<id>${timestamp}-solaris</id>
<includeBaseDirectory>false</includeBaseDirectory>
<formats>
<format>tar.gz</format>
</formats>
<fileSets>
<fileSet>
<directory>target/generated-resources/appassembler/jsw/mycat/bin</directory>
<outputDirectory>mycat/bin</outputDirectory>
<fileMode>0755</fileMode>
<includes>
<include>mycat</include>
<include>wrapper-solaris*</include>
</includes>
</fileSet>
<fileSet>
<directory>target/generated-resources/appassembler/jsw/mycat/lib</directory>
<outputDirectory>mycat/lib</outputDirectory>
<includes>
<include>*.jar</include>
<include>libwrapper-solaris*</include>
</includes>
</fileSet>
<fileSet>
<directory>target/generated-resources/appassembler/jsw/mycat/conf</directory>
<outputDirectory>mycat/conf</outputDirectory>
<includes>
<include>*</include>
</includes>
</fileSet>
<fileSet>
<directory>src/main/resources</directory>
<outputDirectory>mycat/conf</outputDirectory>
<excludes>
<exclude>*.dtd</exclude>
<exclude>log4j*</exclude>
</excludes>
</fileSet>
<fileSet>
<directory>src/main/assembly/conf</directory>
<outputDirectory>mycat/conf</outputDirectory>
</fileSet>
<fileSet>
<directory>${basedir}</directory>
<outputDirectory>mycat/</outputDirectory>
<includes>
<include>version.txt</include>
</includes>
</fileSet>
<fileSet>
<directory>src/main/assembly/bin</directory>
<outputDirectory>mycat/bin</outputDirectory>
<fileMode>0755</fileMode>
<includes>
<include>*.sh</include>
</includes>
</fileSet>
<fileSet>
<directory>${basedir}/logs</directory>
<outputDirectory>mycat/logs</outputDirectory>
<excludes>
<exclude>**/*</exclude>
</excludes>
</fileSet>
<fileSet>
<directory>${basedir}/catlet</directory>
<outputDirectory>mycat/catlet</outputDirectory>
<excludes>
<exclude>**/*</exclude>
</excludes>
</fileSet>
</fileSets>
</assembly>
+46
View File
@@ -0,0 +1,46 @@
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd"
>
<includeBaseDirectory>false</includeBaseDirectory>
<id>${timestamp}-testtool</id>
<formats>
<format>tar.gz</format>
</formats>
<fileSets>
<fileSet>
<directory>src/main/assembly/testtool</directory>
<outputDirectory>mycat/bin</outputDirectory>
<includes>
<include>**/*</include>
</includes>
<fileMode>0755</fileMode>
</fileSet>
<fileSet>
<directory>${basedir}</directory>
<outputDirectory>mycat/</outputDirectory>
<includes>
<include>version.txt</include>
</includes>
</fileSet>
<fileSet>
<directory>target</directory>
<outputDirectory>mycat/lib</outputDirectory>
<includes>
<include>**tests.jar</include>
</includes>
</fileSet>
</fileSets>
<dependencySets>
<dependencySet>
<scope>test</scope>
<outputDirectory>mycat/lib</outputDirectory>
<includes>
<include>mysql-con*</include>
<include>junit*</include>
</includes>
</dependencySet>
</dependencySets>
</assembly>
+79
View File
@@ -0,0 +1,79 @@
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
<id>${timestamp}-unix</id>
<includeBaseDirectory>false</includeBaseDirectory>
<formats>
<format>tar.gz</format>
</formats>
<fileSets>
<fileSet>
<directory>target/generated-resources/appassembler/jsw/mycat/bin</directory>
<outputDirectory>mycat/bin</outputDirectory>
<fileMode>0755</fileMode>
<includes>
<include>mycat</include>
<include>wrapper-aix*</include>
<include>wrapper-hpux*</include>
</includes>
</fileSet>
<fileSet>
<directory>target/generated-resources/appassembler/jsw/mycat/lib</directory>
<outputDirectory>mycat/lib</outputDirectory>
<includes>
<include>*.jar</include>
<include>libwrapper-linux*</include>
</includes>
</fileSet>
<fileSet>
<directory>target/generated-resources/appassembler/jsw/mycat/conf</directory>
<outputDirectory>mycat/conf</outputDirectory>
<includes>
<include>*</include>
</includes>
</fileSet>
<fileSet>
<directory>src/main/resources</directory>
<outputDirectory>mycat/conf</outputDirectory>
<excludes>
<exclude>*.dtd</exclude>
<exclude>log4j*</exclude>
</excludes>
</fileSet>
<fileSet>
<directory>src/main/assembly/conf</directory>
<outputDirectory>mycat/conf</outputDirectory>
</fileSet>
<fileSet>
<directory>${basedir}</directory>
<outputDirectory>mycat/</outputDirectory>
<includes>
<include>version.txt</include>
</includes>
</fileSet>
<fileSet>
<directory>src/main/assembly/bin</directory>
<outputDirectory>mycat/bin</outputDirectory>
<fileMode>0755</fileMode>
<includes>
<include>*.sh</include>
</includes>
</fileSet>
<fileSet>
<directory>${basedir}/logs</directory>
<outputDirectory>mycat/logs</outputDirectory>
<excludes>
<exclude>**/*</exclude>
</excludes>
</fileSet>
<fileSet>
<directory>${basedir}/catlet</directory>
<outputDirectory>mycat/catlet</outputDirectory>
<excludes>
<exclude>**/*</exclude>
</excludes>
</fileSet>
</fileSets>
</assembly>
+77
View File
@@ -0,0 +1,77 @@
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
<id>${timestamp}-win</id>
<includeBaseDirectory>false</includeBaseDirectory>
<formats>
<format>tar.gz</format>
</formats>
<fileSets>
<fileSet>
<directory>target/generated-resources/appassembler/jsw/mycat/bin</directory>
<outputDirectory>mycat/bin</outputDirectory>
<fileMode>0755</fileMode>
<includes>
<include>mycat.bat</include>
<include>wrapper-windows*</include>
</includes>
</fileSet>
<fileSet>
<directory>target/generated-resources/appassembler/jsw/mycat/lib</directory>
<outputDirectory>mycat/lib</outputDirectory>
<includes>
<include>*.jar</include>
<include>wrapper-windows*</include>
</includes>
</fileSet>
<fileSet>
<directory>target/generated-resources/appassembler/jsw/mycat/conf</directory>
<outputDirectory>mycat/conf</outputDirectory>
<includes>
<include>*</include>
</includes>
</fileSet>
<fileSet>
<directory>src/main/resources</directory>
<outputDirectory>mycat/conf</outputDirectory>
<excludes>
<exclude>*.dtd</exclude>
<exclude>log4j*</exclude>
</excludes>
</fileSet>
<fileSet>
<directory>src/main/assembly/conf</directory>
<outputDirectory>mycat/conf</outputDirectory>
</fileSet>
<fileSet>
<directory>${basedir}</directory>
<outputDirectory>mycat/</outputDirectory>
<includes>
<include>version.txt</include>
</includes>
</fileSet>
<fileSet>
<directory>src/main/assembly/bin</directory>
<outputDirectory>mycat/bin</outputDirectory>
<fileMode>0755</fileMode>
<includes>
<include>*.bat</include>
</includes>
</fileSet>
<fileSet>
<directory>${basedir}/logs</directory>
<outputDirectory>mycat/logs</outputDirectory>
<excludes>
<exclude>**/*</exclude>
</excludes>
</fileSet>
<fileSet>
<directory>${basedir}/catlet</directory>
<outputDirectory>mycat/catlet</outputDirectory>
<excludes>
<exclude>**/*</exclude>
</excludes>
</fileSet>
</fileSets>
</assembly>
+32
View File
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d [%-5p][%t] %m %throwable{full} (%C:%F:%L) %n"/>
</Console>
<RollingFile name="RollingFile" fileName="${sys:MYCAT_HOME}/logs/mycat.log"
filePattern="${sys:MYCAT_HOME}/logs/$${date:yyyy-MM}/mycat-%d{MM-dd}-%i.log.gz">
<PatternLayout>
<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %5p [%t] (%l) - %m%n</Pattern>
</PatternLayout>
<Policies>
<OnStartupTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="250 MB"/>
<TimeBasedTriggeringPolicy/>
</Policies>
</RollingFile>
</Appenders>
<Loggers>
<!--<AsyncLogger name="io.mycat" level="info" includeLocation="true" additivity="false">-->
<!--<AppenderRef ref="Console"/>-->
<!--<AppenderRef ref="RollingFile"/>-->
<!--</AsyncLogger>-->
<asyncRoot level="info" includeLocation="true">
<AppenderRef ref="Console" />
<AppenderRef ref="RollingFile"/>
</asyncRoot>
</Loggers>
</Configuration>
@@ -0,0 +1,17 @@
REM check JAVA_HOME & java
set "JAVA_CMD="%JAVA_HOME%/bin/java""
if "%JAVA_HOME%" == "" goto noJavaHome
if exist "%JAVA_HOME%\bin\java.exe" goto mainEntry
:noJavaHome
echo ---------------------------------------------------
echo WARN: JAVA_HOME environment variable is not set.
echo ---------------------------------------------------
set "JAVA_CMD=java"
:mainEntry
REM set HOME_DIR
set "CURR_DIR=%cd%"
cd ..
set "MYCAT_HOME=%cd%"
cd %CURR_DIR%
"%JAVA_CMD%" -Xms256M -Xmx1G -XX:MaxPermSize=64M -DMYCAT_HOME=%MYCAT_HOME% -cp "..\conf;..\lib\*" io.mycat.performance.TestInsertPerf %1 %2 %3 %4 %5
@@ -0,0 +1,17 @@
REM check JAVA_HOME & java
set "JAVA_CMD=%JAVA_HOME%/bin/java"
if "%JAVA_HOME%" == "" goto noJavaHome
if exist "%JAVA_HOME%\bin\java.exe" goto mainEntry
:noJavaHome
echo ---------------------------------------------------
echo WARN: JAVA_HOME environment variable is not set.
echo ---------------------------------------------------
set "JAVA_CMD=java"
:mainEntry
REM set HOME_DIR
set "CURR_DIR=%cd%"
cd ..
set "MYCAT_HOME=%cd%"
cd %CURR_DIR%
"%JAVA_CMD%" -Xms256M -Xmx1G -XX:MaxPermSize=64M -DMYCAT_HOME=%MYCAT_HOME% -cp "..\conf;..\lib\*" io.mycat.performance.TestGlobalTableInsertPerf %1 %2 %3 %4 %5
@@ -0,0 +1,18 @@
#!/bin/bash
echo "check JAVA_HOME & java"
JAVA_CMD=$JAVA_HOME/bin/java
MAIN_CLASS=io.mycat.performance.TestGlobalTableInsertPerf
if [ ! -d "$JAVA_HOME" ]; then
echo ---------------------------------------------------
echo WARN: JAVA_HOME environment variable is not set.
echo ---------------------------------------------------
JAVA_CMD=java
fi
echo "---------set HOME_DIR------------"
CURR_DIR=`pwd`
cd ..
MYCAT_HOME=`pwd`
cd $CURR_DIR
$JAVA_CMD -Xms256M -Xmx1G -XX:MaxPermSize=64M -DMYCAT_HOME=$MYCAT_HOME -cp "$MYCAT_HOME/conf:$MYCAT_HOME/lib/*" $MAIN_CLASS $1 $2 $3 $4 $5
@@ -0,0 +1,17 @@
REM check JAVA_HOME & java
set "JAVA_CMD=%JAVA_HOME%/bin/java"
if "%JAVA_HOME%" == "" goto noJavaHome
if exist "%JAVA_HOME%\bin\java.exe" goto mainEntry
:noJavaHome
echo ---------------------------------------------------
echo WARN: JAVA_HOME environment variable is not set.
echo ---------------------------------------------------
set "JAVA_CMD=java"
:mainEntry
REM set HOME_DIR
set "CURR_DIR=%cd%"
cd ..
set "MYCAT_HOME=%cd%"
cd %CURR_DIR%
"%JAVA_CMD%" -Xms256M -Xmx1G -XX:MaxPermSize=64M -DMYCAT_HOME=%MYCAT_HOME% -cp "..\conf;..\lib\*" io.mycat.performance.TestInsertPerf %1 %2 %3 %4 %5
@@ -0,0 +1,18 @@
#!/bin/bash
echo "check JAVA_HOME & java"
JAVA_CMD=$JAVA_HOME/bin/java
MAIN_CLASS=io.mycat.performance.TestInsertPerf
if [ ! -d "$JAVA_HOME" ]; then
echo ---------------------------------------------------
echo WARN: JAVA_HOME environment variable is not set.
echo ---------------------------------------------------
JAVA_CMD=java
fi
echo "---------set HOME_DIR------------"
CURR_DIR=`pwd`
cd ..
MYCAT_HOME=`pwd`
cd $CURR_DIR
$JAVA_CMD -Xms256M -Xmx1G -XX:MaxPermSize=64M -DMYCAT_HOME=$MYCAT_HOME -cp "$MYCAT_HOME/conf:$MYCAT_HOME/lib/*" $MAIN_CLASS $1 $2 $3 $4 $5
@@ -0,0 +1,17 @@
REM check JAVA_HOME & java
set "JAVA_CMD=%JAVA_HOME%/bin/java"
if "%JAVA_HOME%" == "" goto noJavaHome
if exist "%JAVA_HOME%\bin\java.exe" goto mainEntry
:noJavaHome
echo ---------------------------------------------------
echo WARN: JAVA_HOME environment variable is not set.
echo ---------------------------------------------------
set "JAVA_CMD=java"
:mainEntry
REM set HOME_DIR
set "CURR_DIR=%cd%"
cd ..
set "MYCAT_HOME=%cd%"
cd %CURR_DIR%
"%JAVA_CMD%" -Xms256M -Xmx1G -XX:MaxPermSize=64M -DMYCAT_HOME=%MYCAT_HOME% -cp "..\conf;..\lib\*" io.mycat.performance.TestMergeSelectPerf %1 %2 %3 %4 %5 %6 %7
@@ -0,0 +1,18 @@
#!/bin/bash
echo "check JAVA_HOME & java"
JAVA_CMD=$JAVA_HOME/bin/java
MAIN_CLASS=io.mycat.performance.TestMergeSelectPerf
if [ ! -d "$JAVA_HOME" ]; then
echo ---------------------------------------------------
echo WARN: JAVA_HOME environment variable is not set.
echo ---------------------------------------------------
JAVA_CMD=java
fi
echo "---------set HOME_DIR------------"
CURR_DIR=`pwd`
cd ..
MYCAT_HOME=`pwd`
cd $CURR_DIR
$JAVA_CMD -Xms256M -Xmx1G -XX:MaxPermSize=64M -DMYCAT_HOME=$MYCAT_HOME -cp "$MYCAT_HOME/conf:$MYCAT_HOME/lib/*" $MAIN_CLASS $1 $2 $3 $4 $5 $6 $7
@@ -0,0 +1,17 @@
REM check JAVA_HOME & java
set "JAVA_CMD=%JAVA_HOME%/bin/java"
if "%JAVA_HOME%" == "" goto noJavaHome
if exist "%JAVA_HOME%\bin\java.exe" goto mainEntry
:noJavaHome
echo ---------------------------------------------------
echo WARN: JAVA_HOME environment variable is not set.
echo ---------------------------------------------------
set "JAVA_CMD=java"
:mainEntry
REM set HOME_DIR
set "CURR_DIR=%cd%"
cd ..
set "MYCAT_HOME=%cd%"
cd %CURR_DIR%
"%JAVA_CMD%" -Xms256M -Xmx1G -XX:MaxPermSize=64M -DMYCAT_HOME=%MYCAT_HOME% -cp "..\conf;..\lib\*" io.mycat.performance.TestSelectPerf %1 %2 %3 %4 %5 %6 %7 %8 %9
@@ -0,0 +1,18 @@
#!/bin/bash
echo "check JAVA_HOME & java"
JAVA_CMD=$JAVA_HOME/bin/java
MAIN_CLASS=io.mycat.performance.TestSelectPerf
if [ ! -d "$JAVA_HOME" ]; then
echo ---------------------------------------------------
echo WARN: JAVA_HOME environment variable is not set.
echo ---------------------------------------------------
JAVA_CMD=java
fi
echo "---------set HOME_DIR------------"
CURR_DIR=`pwd`
cd ..
MYCAT_HOME=`pwd`
cd $CURR_DIR
$JAVA_CMD -Xms256M -Xmx1G -XX:MaxPermSize=64M -DMYCAT_HOME=$MYCAT_HOME -cp "$MYCAT_HOME/conf:$MYCAT_HOME/lib/*" $MAIN_CLASS $1 $2 $3 $4 $5 $6 $7 $8 $9
@@ -0,0 +1,17 @@
REM check JAVA_HOME & java
set "JAVA_CMD=%JAVA_HOME%/bin/java"
if "%JAVA_HOME%" == "" goto noJavaHome
if exist "%JAVA_HOME%\bin\java.exe" goto mainEntry
:noJavaHome
echo ---------------------------------------------------
echo WARN: JAVA_HOME environment variable is not set.
echo ---------------------------------------------------
set "JAVA_CMD=java"
:mainEntry
REM set HOME_DIR
set "CURR_DIR=%cd%"
cd ..
set "MYCAT_HOME=%cd%"
cd %CURR_DIR%
"%JAVA_CMD%" -Xms256M -Xmx1G -XX:MaxPermSize=64M -DMYCAT_HOME=%MYCAT_HOME% -cp "..\conf;..\lib\*" io.mycat.performance.TestUpdatePerf %1 %2 %3 %4 %5 %6 %7 %8 %9
@@ -0,0 +1,18 @@
#!/bin/bash
echo "check JAVA_HOME & java"
JAVA_CMD=$JAVA_HOME/bin/java
MAIN_CLASS=io.mycat.performance.TestUpdatePerf
if [ ! -d "$JAVA_HOME" ]; then
echo ---------------------------------------------------
echo WARN: JAVA_HOME environment variable is not set.
echo ---------------------------------------------------
JAVA_CMD=java
fi
echo "---------set HOME_DIR------------"
CURR_DIR=`pwd`
cd ..
MYCAT_HOME=`pwd`
cd $CURR_DIR
$JAVA_CMD -Xms256M -Xmx1G -XX:MaxPermSize=64M -DMYCAT_HOME=$MYCAT_HOME -cp "$MYCAT_HOME/conf:$MYCAT_HOME/lib/*" $MAIN_CLASS $1 $2 $3 $4 $5 $6 $7 $8 $9
+929
View File
@@ -0,0 +1,929 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat;
import com.google.common.io.Files;
import io.mycat.backend.BackendConnection;
import io.mycat.backend.datasource.PhysicalDBNode;
import io.mycat.backend.datasource.PhysicalDBPool;
import io.mycat.backend.mysql.nio.handler.MultiNodeCoordinator;
import io.mycat.backend.mysql.xa.CoordinatorLogEntry;
import io.mycat.backend.mysql.xa.ParticipantLogEntry;
import io.mycat.backend.mysql.xa.TxState;
import io.mycat.backend.mysql.xa.XARollbackCallback;
import io.mycat.backend.mysql.xa.recovery.Repository;
import io.mycat.backend.mysql.xa.recovery.impl.FileSystemRepository;
import io.mycat.buffer.BufferPool;
import io.mycat.buffer.DirectByteBufferPool;
import io.mycat.cache.CacheService;
import io.mycat.config.MycatConfig;
import io.mycat.config.classloader.DynaClassLoader;
import io.mycat.config.loader.zkprocess.comm.ZkConfig;
import io.mycat.config.loader.zkprocess.comm.ZkParamCfg;
import io.mycat.config.model.SchemaConfig;
import io.mycat.config.model.SystemConfig;
import io.mycat.config.model.TableConfig;
import io.mycat.config.table.structure.MySQLTableStructureDetector;
import io.mycat.manager.ManagerConnectionFactory;
import io.mycat.memory.MyCatMemory;
import io.mycat.net.AIOAcceptor;
import io.mycat.net.AIOConnector;
import io.mycat.net.NIOAcceptor;
import io.mycat.net.NIOConnector;
import io.mycat.net.NIOProcessor;
import io.mycat.net.NIOReactorPool;
import io.mycat.net.SocketAcceptor;
import io.mycat.net.SocketConnector;
import io.mycat.route.MyCATSequnceProcessor;
import io.mycat.route.RouteService;
import io.mycat.route.factory.RouteStrategyFactory;
import io.mycat.server.ServerConnectionFactory;
import io.mycat.server.interceptor.SQLInterceptor;
import io.mycat.server.interceptor.impl.GlobalTableUtil;
import io.mycat.sqlengine.OneRawSQLQueryResultHandler;
import io.mycat.sqlengine.SQLJob;
import io.mycat.statistic.SQLRecorder;
import io.mycat.statistic.stat.SqlResultSizeRecorder;
import io.mycat.statistic.stat.UserStat;
import io.mycat.statistic.stat.UserStatAnalyzer;
import io.mycat.util.ExecutorUtil;
import io.mycat.util.NameableExecutor;
import io.mycat.util.TimeUtil;
import java.io.*;
import java.nio.channels.AsynchronousChannelGroup;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import io.mycat.util.ZKUtils;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
/**
* @author mycat
*/
public class MycatServer {
public static final String NAME = "MyCat";
private static final long LOG_WATCH_DELAY = 60000L;
private static final long TIME_UPDATE_PERIOD = 20L;
private static final long DEFAULT_SQL_STAT_RECYCLE_PERIOD = 5 * 1000L;
private static final long DEFAULT_OLD_CONNECTION_CLEAR_PERIOD = 5 * 1000L;
private static final MycatServer INSTANCE = new MycatServer();
private static final Logger LOGGER = LoggerFactory.getLogger("MycatServer");
private static final Repository fileRepository = new FileSystemRepository();
private final RouteService routerService;
private final CacheService cacheService;
private Properties dnIndexProperties;
//AIO连接群组
private AsynchronousChannelGroup[] asyncChannelGroups;
private volatile int channelIndex = 0;
//全局序列号
private final MyCATSequnceProcessor sequnceProcessor = new MyCATSequnceProcessor();
private final DynaClassLoader catletClassLoader;
private final SQLInterceptor sqlInterceptor;
private volatile int nextProcessor;
// System Buffer Pool Instance
private BufferPool bufferPool;
private boolean aio = false;
//XA事务全局ID生成
private final AtomicLong xaIDInc = new AtomicLong();
/**
* Mycat 内存管理类
*/
private MyCatMemory myCatMemory = null;
public static final MycatServer getInstance() {
return INSTANCE;
}
private final MycatConfig config;
private final ScheduledExecutorService scheduler;
private final SQLRecorder sqlRecorder;
private final AtomicBoolean isOnline;
private final long startupTime;
private NIOProcessor[] processors;
private SocketConnector connector;
private NameableExecutor businessExecutor;
private NameableExecutor timerExecutor;
private ListeningExecutorService listeningExecutorService;
private InterProcessMutex dnindexLock;
private long totalNetWorkBufferSize = 0;
private MycatServer() {
//读取文件配置
this.config = new MycatConfig();
//定时线程池,单线程线程池
scheduler = Executors.newSingleThreadScheduledExecutor();
//SQL记录器
this.sqlRecorder = new SQLRecorder(config.getSystem().getSqlRecordCount());
/**
* 是否在线,MyCat manager中有命令控制
* | offline | Change MyCat status to OFF |
* | online | Change MyCat status to ON |
*/
this.isOnline = new AtomicBoolean(true);
//缓存服务初始化
cacheService = new CacheService();
//路由计算初始化
routerService = new RouteService(cacheService);
// load datanode active index from properties
dnIndexProperties = loadDnIndexProps();
try {
//SQL解析器
sqlInterceptor = (SQLInterceptor) Class.forName(
config.getSystem().getSqlInterceptor()).newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
//catlet加载器
catletClassLoader = new DynaClassLoader(SystemConfig.getHomePath()
+ File.separator + "catlet", config.getSystem().getCatletClassCheckSeconds());
//记录启动时间
this.startupTime = TimeUtil.currentTimeMillis();
if(isUseZkSwitch()) {
String path= ZKUtils.getZKBasePath()+"lock/dnindex.lock";
dnindexLock = new InterProcessMutex(ZKUtils.getConnection(), path);
}
}
public long getTotalNetWorkBufferSize() {
return totalNetWorkBufferSize;
}
public BufferPool getBufferPool() {
return bufferPool;
}
public NameableExecutor getTimerExecutor() {
return timerExecutor;
}
public DynaClassLoader getCatletClassLoader() {
return catletClassLoader;
}
public MyCATSequnceProcessor getSequnceProcessor() {
return sequnceProcessor;
}
public SQLInterceptor getSqlInterceptor() {
return sqlInterceptor;
}
public String genXATXID() {
long seq = this.xaIDInc.incrementAndGet();
if (seq < 0) {
synchronized (xaIDInc) {
if ( xaIDInc.get() < 0 ) {
xaIDInc.set(0);
}
seq = xaIDInc.incrementAndGet();
}
}
return "'Mycat." + this.getConfig().getSystem().getMycatNodeId() + "." + seq + "'";
}
public MyCatMemory getMyCatMemory() {
return myCatMemory;
}
/**
* get next AsynchronousChannel ,first is exclude if multi
* AsynchronousChannelGroups
*
* @return
*/
public AsynchronousChannelGroup getNextAsyncChannelGroup() {
if (asyncChannelGroups.length == 1) {
return asyncChannelGroups[0];
} else {
int index = (++channelIndex) % asyncChannelGroups.length;
if (index == 0) {
++channelIndex;
return asyncChannelGroups[1];
} else {
return asyncChannelGroups[index];
}
}
}
public MycatConfig getConfig() {
return config;
}
public void beforeStart() {
String home = SystemConfig.getHomePath();
//ZkConfig.instance().initZk();
}
public void startup() throws IOException {
SystemConfig system = config.getSystem();
int processorCount = system.getProcessors();
// server startup
LOGGER.info("===============================================");
LOGGER.info(NAME + " is ready to startup ...");
String inf = "Startup processors ...,total processors:"
+ system.getProcessors() + ",aio thread pool size:"
+ system.getProcessorExecutor()
+ " \r\n each process allocated socket buffer pool "
+ " bytes ,a page size:"
+ system.getBufferPoolPageSize()
+ " a page's chunk number(PageSize/ChunkSize) is:"
+ (system.getBufferPoolPageSize()
/system.getBufferPoolChunkSize())
+ " buffer page's number is:"
+ system.getBufferPoolPageNumber();
LOGGER.info(inf);
LOGGER.info("sysconfig params:" + system.toString());
// startup manager
ManagerConnectionFactory mf = new ManagerConnectionFactory();
ServerConnectionFactory sf = new ServerConnectionFactory();
SocketAcceptor manager = null;
SocketAcceptor server = null;
aio = (system.getUsingAIO() == 1);
// startup processors
int threadPoolSize = system.getProcessorExecutor();
processors = new NIOProcessor[processorCount];
// a page size
int bufferPoolPageSize = system.getBufferPoolPageSize();
// total page number
short bufferPoolPageNumber = system.getBufferPoolPageNumber();
//minimum allocation unit
short bufferPoolChunkSize = system.getBufferPoolChunkSize();
int socketBufferLocalPercent = system.getProcessorBufferLocalPercent();
int bufferPoolType = system.getProcessorBufferPoolType();
switch (bufferPoolType){
case 0:
bufferPool = new DirectByteBufferPool(bufferPoolPageSize,bufferPoolChunkSize,
bufferPoolPageNumber,system.getFrontSocketSoRcvbuf());
totalNetWorkBufferSize = bufferPoolPageSize*bufferPoolPageNumber;
break;
case 1:
/**
* todo 对应权威指南修改:
*
* bytebufferarena由6个bytebufferlist组成,这六个list有减少内存碎片的机制
* 每个bytebufferlist由多个bytebufferchunk组成,每个list也有减少内存碎片的机制
* 每个bytebufferchunk由多个page组成,平衡二叉树管理内存使用状态,计算灵活
* 设置的pagesize对应bytebufferarena里面的每个bytebufferlist的每个bytebufferchunk的buffer长度
* bufferPoolChunkSize对应每个bytebufferchunk的每个page的长度
* bufferPoolPageNumber对应每个bytebufferlist有多少个bytebufferchunk
*/
totalNetWorkBufferSize = 6*bufferPoolPageSize * bufferPoolPageNumber;
break;
default:
bufferPool = new DirectByteBufferPool(bufferPoolPageSize,bufferPoolChunkSize,
bufferPoolPageNumber,system.getFrontSocketSoRcvbuf());;
totalNetWorkBufferSize = bufferPoolPageSize*bufferPoolPageNumber;
}
/**
* Off Heap For Merge/Order/Group/Limit 初始化
*/
if(system.getUseOffHeapForMerge() == 1){
try {
myCatMemory = new MyCatMemory(system,totalNetWorkBufferSize);
} catch (NoSuchFieldException e) {
LOGGER .error("NoSuchFieldException",e);
} catch (IllegalAccessException e) {
LOGGER.error("Error",e);
}
}
businessExecutor = ExecutorUtil.create("BusinessExecutor",
threadPoolSize);
timerExecutor = ExecutorUtil.create("Timer", system.getTimerExecutor());
listeningExecutorService = MoreExecutors.listeningDecorator(businessExecutor);
for (int i = 0; i < processors.length; i++) {
processors[i] = new NIOProcessor("Processor" + i, bufferPool,
businessExecutor);
}
if (aio) {
LOGGER.info("using aio network handler ");
asyncChannelGroups = new AsynchronousChannelGroup[processorCount];
// startup connector
connector = new AIOConnector();
for (int i = 0; i < processors.length; i++) {
asyncChannelGroups[i] = AsynchronousChannelGroup.withFixedThreadPool(processorCount,
new ThreadFactory() {
private int inx = 1;
@Override
public Thread newThread(Runnable r) {
Thread th = new Thread(r);
//TODO
th.setName(DirectByteBufferPool.LOCAL_BUF_THREAD_PREX + "AIO" + (inx++));
LOGGER.info("created new AIO thread "+ th.getName());
return th;
}
}
);
}
manager = new AIOAcceptor(NAME + "Manager", system.getBindIp(),
system.getManagerPort(), mf, this.asyncChannelGroups[0]);
// startup server
server = new AIOAcceptor(NAME + "Server", system.getBindIp(),
system.getServerPort(), sf, this.asyncChannelGroups[0]);
} else {
LOGGER.info("using nio network handler ");
NIOReactorPool reactorPool = new NIOReactorPool(
DirectByteBufferPool.LOCAL_BUF_THREAD_PREX + "NIOREACTOR",
processors.length);
connector = new NIOConnector(DirectByteBufferPool.LOCAL_BUF_THREAD_PREX + "NIOConnector", reactorPool);
((NIOConnector) connector).start();
manager = new NIOAcceptor(DirectByteBufferPool.LOCAL_BUF_THREAD_PREX + NAME
+ "Manager", system.getBindIp(), system.getManagerPort(), mf, reactorPool);
server = new NIOAcceptor(DirectByteBufferPool.LOCAL_BUF_THREAD_PREX + NAME
+ "Server", system.getBindIp(), system.getServerPort(), sf, reactorPool);
}
// manager start
manager.start();
LOGGER.info(manager.getName() + " is started and listening on " + manager.getPort());
server.start();
// server started
LOGGER.info(server.getName() + " is started and listening on " + server.getPort());
LOGGER.info("===============================================");
// init datahost
Map<String, PhysicalDBPool> dataHosts = config.getDataHosts();
LOGGER.info("Initialize dataHost ...");
for (PhysicalDBPool node : dataHosts.values()) {
String index = dnIndexProperties.getProperty(node.getHostName(),"0");
if (!"0".equals(index)) {
LOGGER.info("init datahost: " + node.getHostName() + " to use datasource index:" + index);
}
node.init(Integer.parseInt(index));
node.startHeartbeat();
}
long dataNodeIldeCheckPeriod = system.getDataNodeIdleCheckPeriod();
scheduler.scheduleAtFixedRate(updateTime(), 0L, TIME_UPDATE_PERIOD,TimeUnit.MILLISECONDS);
scheduler.scheduleAtFixedRate(processorCheck(), 0L, system.getProcessorCheckPeriod(),TimeUnit.MILLISECONDS);
scheduler.scheduleAtFixedRate(dataNodeConHeartBeatCheck(dataNodeIldeCheckPeriod), 0L, dataNodeIldeCheckPeriod,TimeUnit.MILLISECONDS);
scheduler.scheduleAtFixedRate(dataNodeHeartbeat(), 0L, system.getDataNodeHeartbeatPeriod(),TimeUnit.MILLISECONDS);
scheduler.scheduleAtFixedRate(dataSourceOldConsClear(), 0L, DEFAULT_OLD_CONNECTION_CLEAR_PERIOD, TimeUnit.MILLISECONDS);
scheduler.schedule(catletClassClear(), 30000,TimeUnit.MILLISECONDS);
if(system.getCheckTableConsistency()==1) {
scheduler.scheduleAtFixedRate(tableStructureCheck(), 0L, system.getCheckTableConsistencyPeriod(), TimeUnit.MILLISECONDS);
}
if(system.getUseSqlStat()==1) {
scheduler.scheduleAtFixedRate(recycleSqlStat(), 0L, DEFAULT_SQL_STAT_RECYCLE_PERIOD, TimeUnit.MILLISECONDS);
}
if(system.getUseGlobleTableCheck() == 1){ // 全局表一致性检测是否开启
scheduler.scheduleAtFixedRate(glableTableConsistencyCheck(), 0L, system.getGlableTableCheckPeriod(), TimeUnit.MILLISECONDS);
}
//定期清理结果集排行榜,控制拒绝策略
scheduler.scheduleAtFixedRate(resultSetMapClear(),0L, system.getClearBigSqLResultSetMapMs(),TimeUnit.MILLISECONDS);
RouteStrategyFactory.init();
// new Thread(tableStructureCheck()).start();
//XA Init recovery Log
LOGGER.info("===============================================");
LOGGER.info("Perform XA recovery log ...");
performXARecoveryLog();
if(isUseZkSwitch()) {
//首次启动如果发现zk上dnindex为空,则将本地初始化上zk
try {
File file = new File(SystemConfig.getHomePath(), "conf" + File.separator + "dnindex.properties");
dnindexLock.acquire(30, TimeUnit.SECONDS);
String path = ZKUtils.getZKBasePath() + "bindata/dnindex.properties";
CuratorFramework zk = ZKUtils.getConnection();
if (zk.checkExists().forPath(path) == null) {
zk.create().creatingParentsIfNeeded().forPath(path, Files.toByteArray(file));
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
try {
dnindexLock.release();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
public void reloadDnIndex()
{
if(MycatServer.getInstance().getProcessors()==null) return;
// load datanode active index from properties
dnIndexProperties = loadDnIndexProps();
// init datahost
Map<String, PhysicalDBPool> dataHosts = config.getDataHosts();
LOGGER.info("reInitialize dataHost ...");
for (PhysicalDBPool node : dataHosts.values()) {
String index = dnIndexProperties.getProperty(node.getHostName(),"0");
if (!"0".equals(index)) {
LOGGER.info("reinit datahost: " + node.getHostName() + " to use datasource index:" + index);
}
node.switchSource(Integer.parseInt(index),true,"reload dnindex");
}
}
private Runnable catletClassClear() {
return new Runnable() {
@Override
public void run() {
try {
catletClassLoader.clearUnUsedClass();
} catch (Exception e) {
LOGGER.warn("catletClassClear err " + e);
}
};
};
}
/**
* 清理 reload @@config_all 后,老的 connection 连接
* @return
*/
private Runnable dataSourceOldConsClear() {
return new Runnable() {
@Override
public void run() {
timerExecutor.execute(new Runnable() {
@Override
public void run() {
long sqlTimeout = MycatServer.getInstance().getConfig().getSystem().getSqlExecuteTimeout() * 1000L;
//根据 lastTime 确认事务的执行, 超过 sqlExecuteTimeout 阀值 close connection
long currentTime = TimeUtil.currentTimeMillis();
Iterator<BackendConnection> iter = NIOProcessor.backends_old.iterator();
while( iter.hasNext() ) {
BackendConnection con = iter.next();
long lastTime = con.getLastTime();
if ( currentTime - lastTime > sqlTimeout ) {
con.close("clear old backend connection ...");
iter.remove();
}
}
}
});
};
};
}
/**
* 在bufferpool使用率大于使用率阈值时不清理
* 在bufferpool使用率小于使用率阈值时清理大结果集清单内容
*
*/
private Runnable resultSetMapClear() {
return new Runnable() {
@Override
public void run() {
try {
BufferPool bufferPool=getBufferPool();
long bufferSize=bufferPool.size();
long bufferCapacity=bufferPool.capacity();
long bufferUsagePercent=(bufferCapacity-bufferSize)*100/bufferCapacity;
if(bufferUsagePercent<config.getSystem().getBufferUsagePercent()){
Map<String, UserStat> map =UserStatAnalyzer.getInstance().getUserStatMap();
Set<String> userSet=config.getUsers().keySet();
for (String user : userSet) {
UserStat userStat = map.get(user);
if(userStat!=null){
SqlResultSizeRecorder recorder=userStat.getSqlResultSizeRecorder();
//System.out.println(recorder.getSqlResultSet().size());
recorder.clearSqlResultSet();
}
}
}
} catch (Exception e) {
LOGGER.warn("resultSetMapClear err " + e);
}
};
};
}
private Properties loadDnIndexProps() {
Properties prop = new Properties();
File file = new File(SystemConfig.getHomePath(), "conf" + File.separator + "dnindex.properties");
if (!file.exists()) {
return prop;
}
FileInputStream filein = null;
try {
filein = new FileInputStream(file);
prop.load(filein);
} catch (Exception e) {
LOGGER.warn("load DataNodeIndex err:" + e);
} finally {
if (filein != null) {
try {
filein.close();
} catch (IOException e) {
}
}
}
return prop;
}
/**
* save cur datanode index to properties file
*
* @param
* @param curIndex
*/
public synchronized void saveDataHostIndex(String dataHost, int curIndex) {
File file = new File(SystemConfig.getHomePath(), "conf" + File.separator + "dnindex.properties");
FileOutputStream fileOut = null;
try {
String oldIndex = dnIndexProperties.getProperty(dataHost);
String newIndex = String.valueOf(curIndex);
if (newIndex.equals(oldIndex)) {
return;
}
dnIndexProperties.setProperty(dataHost, newIndex);
LOGGER.info("save DataHost index " + dataHost + " cur index " + curIndex);
File parent = file.getParentFile();
if (parent != null && !parent.exists()) {
parent.mkdirs();
}
fileOut = new FileOutputStream(file);
dnIndexProperties.store(fileOut, "update");
if(isUseZkSwitch()) {
// save to zk
try {
dnindexLock.acquire(30,TimeUnit.SECONDS) ;
String path = ZKUtils.getZKBasePath() + "bindata/dnindex.properties";
CuratorFramework zk = ZKUtils.getConnection();
if(zk.checkExists().forPath(path)==null) {
zk.create().creatingParentsIfNeeded().forPath(path, Files.toByteArray(file));
} else{
byte[] data= zk.getData().forPath(path);
ByteArrayOutputStream out=new ByteArrayOutputStream();
Properties properties=new Properties();
properties.load(new ByteArrayInputStream(data));
if(!String.valueOf(curIndex).equals(properties.getProperty(dataHost))) {
properties.setProperty(dataHost, String.valueOf(curIndex));
properties.store(out, "update");
zk.setData().forPath(path, out.toByteArray());
}
}
}finally {
dnindexLock.release();
}
}
} catch (Exception e) {
LOGGER.warn("saveDataNodeIndex err:", e);
} finally {
if (fileOut != null) {
try {
fileOut.close();
} catch (IOException e) {
}
}
}
}
private boolean isUseZkSwitch()
{
MycatConfig mycatConfig=config;
boolean isUseZkSwitch= mycatConfig.getSystem().isUseZKSwitch();
String loadZk=ZkConfig.getInstance().getValue(ZkParamCfg.ZK_CFG_FLAG);
return (isUseZkSwitch&&"true".equalsIgnoreCase(loadZk)) ;
}
public RouteService getRouterService() {
return routerService;
}
public CacheService getCacheService() {
return cacheService;
}
public NameableExecutor getBusinessExecutor() {
return businessExecutor;
}
public RouteService getRouterservice() {
return routerService;
}
public NIOProcessor nextProcessor() {
int i = ++nextProcessor;
if (i >= processors.length) {
i = nextProcessor = 0;
}
return processors[i];
}
public NIOProcessor[] getProcessors() {
return processors;
}
public SocketConnector getConnector() {
return connector;
}
public SQLRecorder getSqlRecorder() {
return sqlRecorder;
}
public long getStartupTime() {
return startupTime;
}
public boolean isOnline() {
return isOnline.get();
}
public void offline() {
isOnline.set(false);
}
public void online() {
isOnline.set(true);
}
// 系统时间定时更新任务
private Runnable updateTime() {
return new Runnable() {
@Override
public void run() {
TimeUtil.update();
}
};
}
// 处理器定时检查任务
private Runnable processorCheck() {
return new Runnable() {
@Override
public void run() {
timerExecutor.execute(new Runnable() {
@Override
public void run() {
try {
for (NIOProcessor p : processors) {
p.checkBackendCons();
}
} catch (Exception e) {
LOGGER.warn("checkBackendCons caught err:" + e);
}
}
});
timerExecutor.execute(new Runnable() {
@Override
public void run() {
try {
for (NIOProcessor p : processors) {
p.checkFrontCons();
}
} catch (Exception e) {
LOGGER.warn("checkFrontCons caught err:" + e);
}
}
});
}
};
}
// 数据节点定时连接空闲超时检查任务
private Runnable dataNodeConHeartBeatCheck(final long heartPeriod) {
return new Runnable() {
@Override
public void run() {
timerExecutor.execute(new Runnable() {
@Override
public void run() {
Map<String, PhysicalDBPool> nodes = config.getDataHosts();
for (PhysicalDBPool node : nodes.values()) {
node.heartbeatCheck(heartPeriod);
}
/*
Map<String, PhysicalDBPool> _nodes = config.getBackupDataHosts();
if (_nodes != null) {
for (PhysicalDBPool node : _nodes.values()) {
node.heartbeatCheck(heartPeriod);
}
}*/
}
});
}
};
}
// 数据节点定时心跳任务
private Runnable dataNodeHeartbeat() {
return new Runnable() {
@Override
public void run() {
timerExecutor.execute(new Runnable() {
@Override
public void run() {
Map<String, PhysicalDBPool> nodes = config.getDataHosts();
for (PhysicalDBPool node : nodes.values()) {
node.doHeartbeat();
}
}
});
}
};
}
//定时清理保存SqlStat中的数据
private Runnable recycleSqlStat(){
return new Runnable() {
@Override
public void run() {
Map<String, UserStat> statMap = UserStatAnalyzer.getInstance().getUserStatMap();
for (UserStat userStat : statMap.values()) {
userStat.getSqlLastStat().recycle();
userStat.getSqlRecorder().recycle();
userStat.getSqlHigh().recycle();
userStat.getSqlLargeRowStat().recycle();
}
}
};
}
//定时检查不同分片表结构一致性
private Runnable tableStructureCheck(){
return new MySQLTableStructureDetector();
}
// 全局表一致性检查任务
private Runnable glableTableConsistencyCheck() {
return new Runnable() {
@Override
public void run() {
timerExecutor.execute(new Runnable() {
@Override
public void run() {
GlobalTableUtil.consistencyCheck();
}
});
}
};
}
//XA recovery log check
private void performXARecoveryLog() {
//fetch the recovery log
CoordinatorLogEntry[] coordinatorLogEntries = getCoordinatorLogEntries();
for(int i=0; i<coordinatorLogEntries.length; i++){
CoordinatorLogEntry coordinatorLogEntry = coordinatorLogEntries[i];
boolean needRollback = false;
for(int j=0; j<coordinatorLogEntry.participants.length; j++) {
ParticipantLogEntry participantLogEntry = coordinatorLogEntry.participants[j];
if (participantLogEntry.txState == TxState.TX_PREPARED_STATE){
needRollback = true;
break;
}
}
if(needRollback){
for(int j=0; j<coordinatorLogEntry.participants.length; j++){
ParticipantLogEntry participantLogEntry = coordinatorLogEntry.participants[j];
//XA rollback
String xacmd = "XA ROLLBACK "+ coordinatorLogEntry.id +';';
OneRawSQLQueryResultHandler resultHandler = new OneRawSQLQueryResultHandler( new String[0], new XARollbackCallback());
outloop:
for (SchemaConfig schema : MycatServer.getInstance().getConfig().getSchemas().values()) {
for (TableConfig table : schema.getTables().values()) {
for (String dataNode : table.getDataNodes()) {
PhysicalDBNode dn = MycatServer.getInstance().getConfig().getDataNodes().get(dataNode);
if (dn.getDbPool().getSource().getConfig().getIp().equals(participantLogEntry.uri)
&& dn.getDatabase().equals(participantLogEntry.resourceName)) {
//XA STATE ROLLBACK
participantLogEntry.txState = TxState.TX_ROLLBACKED_STATE;
SQLJob sqlJob = new SQLJob(xacmd, dn.getDatabase(), resultHandler, dn.getDbPool().getSource());
sqlJob.run();
LOGGER.debug(String.format("[XA ROLLBACK] [%s] Host:[%s] schema:[%s]", xacmd, dn.getName(), dn.getDatabase()));
break outloop;
}
}
}
}
}
}
}
//init into in memory cached
for(int i=0;i<coordinatorLogEntries.length;i++){
MultiNodeCoordinator.inMemoryRepository.put(coordinatorLogEntries[i].id,coordinatorLogEntries[i]);
}
//discard the recovery log
MultiNodeCoordinator.fileRepository.writeCheckpoint(MultiNodeCoordinator.inMemoryRepository.getAllCoordinatorLogEntries());
}
/** covert the collection to array **/
private CoordinatorLogEntry[] getCoordinatorLogEntries(){
Collection<CoordinatorLogEntry> allCoordinatorLogEntries = fileRepository.getAllCoordinatorLogEntries();
if(allCoordinatorLogEntries == null){return new CoordinatorLogEntry[0];}
if(allCoordinatorLogEntries.size()==0){return new CoordinatorLogEntry[0];}
return allCoordinatorLogEntries.toArray(new CoordinatorLogEntry[allCoordinatorLogEntries.size()]);
}
public boolean isAIO() {
return aio;
}
public ListeningExecutorService getListeningExecutorService() {
return listeningExecutorService;
}
public static void main(String[] args) throws Exception {
String path = ZKUtils.getZKBasePath() + "bindata";
CuratorFramework zk = ZKUtils.getConnection();
if(zk.checkExists().forPath(path)==null);
byte[] data= zk.getData().forPath(path);
System.out.println(data.length);
}
}
+37
View File
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat;
import java.util.Date;
/**
* @author mycat
*/
public final class MycatShutdown {
public static void main(String[] args) {
System.out.println(new Date() + ",server shutdown!");
}
}
+66
View File
@@ -0,0 +1,66 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.mycat.config.loader.zkprocess.comm.ZkConfig;
import io.mycat.config.model.SystemConfig;
/**
* @author mycat
*/
public final class MycatStartup {
private static final String dateFormat = "yyyy-MM-dd HH:mm:ss";
private static final Logger LOGGER = LoggerFactory.getLogger(MycatStartup.class);
public static void main(String[] args) {
//use zk ?
ZkConfig.getInstance().initZk();
try {
String home = SystemConfig.getHomePath();
if (home == null) {
System.out.println(SystemConfig.SYS_HOME + " is not set.");
System.exit(-1);
}
// init
MycatServer server = MycatServer.getInstance();
server.beforeStart();
// startup
server.startup();
System.out.println("MyCAT Server startup successfully. see logs in logs/mycat.log");
} catch (Exception e) {
SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
LOGGER.error(sdf.format(new Date()) + " startup error", e);
System.exit(-1);
}
}
}
@@ -0,0 +1,65 @@
package io.mycat.backend;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import io.mycat.backend.mysql.nio.handler.ResponseHandler;
import io.mycat.net.ClosableConnection;
import io.mycat.route.RouteResultsetNode;
import io.mycat.server.ServerConnection;
public interface BackendConnection extends ClosableConnection {
public boolean isModifiedSQLExecuted();
public boolean isFromSlaveDB();
public String getSchema();
public void setSchema(String newSchema);
public long getLastTime();
public boolean isClosedOrQuit();
public void setAttachment(Object attachment);
public void quit();
public void setLastTime(long currentTimeMillis);
public void release();
public boolean setResponseHandler(ResponseHandler commandHandler);
public void commit();
public void query(String sql) throws UnsupportedEncodingException;
public Object getAttachment();
// public long getThreadId();
public void execute(RouteResultsetNode node, ServerConnection source,
boolean autocommit) throws IOException;
public void recordSql(String host, String schema, String statement);
public boolean syncAndExcute();
public void rollback();
public boolean isBorrowed();
public void setBorrowed(boolean borrowed);
public int getTxIsolation();
public boolean isAutocommit();
public long getId();
public void discardClose(String reason);
}
+136
View File
@@ -0,0 +1,136 @@
package io.mycat.backend;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import io.mycat.MycatServer;
import io.mycat.backend.datasource.PhysicalDatasource;
import io.mycat.backend.jdbc.JDBCConnection;
import io.mycat.backend.mysql.nio.MySQLConnection;
import io.mycat.net.NIOProcessor;
public class ConMap {
// key -schema
private final ConcurrentHashMap<String, ConQueue> items = new ConcurrentHashMap<String, ConQueue>();
public ConQueue getSchemaConQueue(String schema) {
ConQueue queue = items.get(schema);
if (queue == null) {
ConQueue newQueue = new ConQueue();
queue = items.putIfAbsent(schema, newQueue);
return (queue == null) ? newQueue : queue;
}
return queue;
}
public BackendConnection tryTakeCon(final String schema, boolean autoCommit) {
final ConQueue queue = items.get(schema);
BackendConnection con = tryTakeCon(queue, autoCommit);
if (con != null) {
return con;
} else {
for (ConQueue queue2 : items.values()) {
if (queue != queue2) {
con = tryTakeCon(queue2, autoCommit);
if (con != null) {
return con;
}
}
}
}
return null;
}
private BackendConnection tryTakeCon(ConQueue queue, boolean autoCommit) {
BackendConnection con = null;
if (queue != null && ((con = queue.takeIdleCon(autoCommit)) != null)) {
return con;
} else {
return null;
}
}
public Collection<ConQueue> getAllConQueue() {
return items.values();
}
public int getActiveCountForSchema(String schema,
PhysicalDatasource dataSouce) {
int total = 0;
for (NIOProcessor processor : MycatServer.getInstance().getProcessors()) {
for (BackendConnection con : processor.getBackends().values()) {
if (con instanceof MySQLConnection) {
MySQLConnection mysqlCon = (MySQLConnection) con;
if (mysqlCon.getSchema().equals(schema)
&& mysqlCon.getPool() == dataSouce
&& mysqlCon.isBorrowed()) {
total++;
}
}else if (con instanceof JDBCConnection) {
JDBCConnection jdbcCon = (JDBCConnection) con;
if (jdbcCon.getSchema().equals(schema) && jdbcCon.getPool() == dataSouce
&& jdbcCon.isBorrowed()) {
total++;
}
}
}
}
return total;
}
public int getActiveCountForDs(PhysicalDatasource dataSouce) {
int total = 0;
for (NIOProcessor processor : MycatServer.getInstance().getProcessors()) {
for (BackendConnection con : processor.getBackends().values()) {
if (con instanceof MySQLConnection) {
MySQLConnection mysqlCon = (MySQLConnection) con;
if (mysqlCon.getPool() == dataSouce
&& mysqlCon.isBorrowed() && !mysqlCon.isClosed()) {
total++;
}
} else if (con instanceof JDBCConnection) {
JDBCConnection jdbcCon = (JDBCConnection) con;
if (jdbcCon.getPool() == dataSouce
&& jdbcCon.isBorrowed() && !jdbcCon.isClosed()) {
total++;
}
}
}
}
return total;
}
public void clearConnections(String reason, PhysicalDatasource dataSouce) {
for (NIOProcessor processor : MycatServer.getInstance().getProcessors()) {
ConcurrentMap<Long, BackendConnection> map = processor.getBackends();
Iterator<Entry<Long, BackendConnection>> itor = map.entrySet().iterator();
while (itor.hasNext()) {
Entry<Long, BackendConnection> entry = itor.next();
BackendConnection con = entry.getValue();
if (con instanceof MySQLConnection) {
if (((MySQLConnection) con).getPool() == dataSouce) {
con.close(reason);
itor.remove();
}
}else if((con instanceof JDBCConnection)
&& (((JDBCConnection) con).getPool() == dataSouce)){
con.close(reason);
itor.remove();
}
}
}
items.clear();
}
}
@@ -0,0 +1,82 @@
package io.mycat.backend;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentLinkedQueue;
public class ConQueue {
private final ConcurrentLinkedQueue<BackendConnection> autoCommitCons = new ConcurrentLinkedQueue<BackendConnection>();
private final ConcurrentLinkedQueue<BackendConnection> manCommitCons = new ConcurrentLinkedQueue<BackendConnection>();
private long executeCount;
public BackendConnection takeIdleCon(boolean autoCommit) {
ConcurrentLinkedQueue<BackendConnection> f1 = autoCommitCons;
ConcurrentLinkedQueue<BackendConnection> f2 = manCommitCons;
if (!autoCommit) {
f1 = manCommitCons;
f2 = autoCommitCons;
}
BackendConnection con = f1.poll();
if (con == null || con.isClosedOrQuit()) {
con = f2.poll();
}
if (con == null || con.isClosedOrQuit()) {
return null;
} else {
return con;
}
}
public long getExecuteCount() {
return executeCount;
}
public void incExecuteCount() {
this.executeCount++;
}
public void removeCon(BackendConnection con) {
if (!autoCommitCons.remove(con)) {
manCommitCons.remove(con);
}
}
public boolean isSameCon(BackendConnection con) {
if (autoCommitCons.contains(con)) {
return true;
} else if (manCommitCons.contains(con)) {
return true;
}
return false;
}
public ConcurrentLinkedQueue<BackendConnection> getAutoCommitCons() {
return autoCommitCons;
}
public ConcurrentLinkedQueue<BackendConnection> getManCommitCons() {
return manCommitCons;
}
public ArrayList<BackendConnection> getIdleConsToClose(int count) {
ArrayList<BackendConnection> readyCloseCons = new ArrayList<BackendConnection>(
count);
while (!manCommitCons.isEmpty() && readyCloseCons.size() < count) {
BackendConnection theCon = manCommitCons.poll();
if (theCon != null) {
readyCloseCons.add(theCon);
}
}
while (!autoCommitCons.isEmpty() && readyCloseCons.size() < count) {
BackendConnection theCon = autoCommitCons.poll();
if (theCon != null) {
readyCloseCons.add(theCon);
}
}
return readyCloseCons;
}
}
@@ -0,0 +1,98 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat.backend;
/**
* connection metadata info
*
* @author wuzhih
*
*/
public class ConnectionMeta {
private final String schema;
private final String charset;
private final int txIsolation;
private final boolean autocommit;
public ConnectionMeta(String schema, String charset, int txIsolation,
boolean autocommit) {
super();
this.schema = schema;
this.charset = charset;
this.txIsolation = txIsolation;
this.autocommit = autocommit;
}
public String getSchema() {
return schema;
}
// public String getCharset() {
// return charset;
// }
//
// public int getTxIsolation() {
// return txIsolation;
// }
//
// public boolean isAutocommit() {
// return autocommit;
// }
public boolean isSameSchema(BackendConnection theCon)
{
return theCon.getSchema().equals(schema);
}
/**
* get metadata similarity
*
* @param theCon
* @return
*/
public int getMetaSimilarity(BackendConnection theCon) {
int result = 0;
if (schema == null || schema.equals(theCon.getSchema())) {
result++;
}
if (charset == null || charset.equals(theCon.getCharset())) {
result++;
}
if (txIsolation == -1 || txIsolation == theCon.getTxIsolation()) {
result++;
}
if (autocommit == theCon.isAutocommit()) {
result++;
}
return result;
}
@Override
public String toString() {
return "ConnectionMeta [schema=" + schema + ", charset=" + charset
+ ", txIsolation=" + txIsolation + ", autocommit=" + autocommit
+ "]";
}
}
@@ -0,0 +1,160 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat.backend.datasource;
import org.slf4j.Logger; import org.slf4j.LoggerFactory;
import io.mycat.backend.BackendConnection;
import io.mycat.backend.mysql.nio.handler.ResponseHandler;
import io.mycat.route.RouteResultsetNode;
public class PhysicalDBNode {
protected static final Logger LOGGER = LoggerFactory
.getLogger(PhysicalDBNode.class);
protected final String name;
protected final String database;
protected final PhysicalDBPool dbPool;
public PhysicalDBNode(String hostName, String database,
PhysicalDBPool dbPool) {
this.name = hostName;
this.database = database;
this.dbPool = dbPool;
}
public String getName() {
return name;
}
public PhysicalDBPool getDbPool() {
return dbPool;
}
public String getDatabase() {
return database;
}
/**
* get connection from the same datasource
*
* @param exitsCon
* @throws Exception
*/
public void getConnectionFromSameSource(String schema,boolean autocommit,
BackendConnection exitsCon, ResponseHandler handler,
Object attachment) throws Exception {
PhysicalDatasource ds = this.dbPool.findDatasouce(exitsCon);
if (ds == null) {
throw new RuntimeException(
"can't find exits connection,maybe fininshed " + exitsCon);
} else {
ds.getConnection(schema,autocommit, handler, attachment);
}
}
private void checkRequest(String schema){
if (schema != null
&& !schema.equals(this.database)) {
throw new RuntimeException(
"invalid param ,connection request db is :"
+ schema + " and datanode db is "
+ this.database);
}
if (!dbPool.isInitSuccess()) {
dbPool.init(dbPool.activedIndex);
}
}
public void getConnection(String schema,boolean autoCommit, RouteResultsetNode rrs,
ResponseHandler handler, Object attachment) throws Exception {
checkRequest(schema);
if (dbPool.isInitSuccess()) {
LOGGER.debug("rrs.getRunOnSlave() " + rrs.getRunOnSlave());
if(rrs.getRunOnSlave() != null){ // 带有 /*db_type=master/slave*/ 注解
// 强制走 slave
if(rrs.getRunOnSlave()){
LOGGER.debug("rrs.isHasBlanceFlag() " + rrs.isHasBlanceFlag());
if (rrs.isHasBlanceFlag()) { // 带有 /*balance*/ 注解(目前好像只支持一个注解...)
dbPool.getReadBanlanceCon(schema,autoCommit,handler, attachment, this.database);
}else{ // 没有 /*balance*/ 注解
LOGGER.debug("rrs.isHasBlanceFlag()" + rrs.isHasBlanceFlag());
if(!dbPool.getReadCon(schema, autoCommit, handler, attachment, this.database)){
LOGGER.warn("Do not have slave connection to use, use master connection instead.");
PhysicalDatasource writeSource=dbPool.getSource();
//记录写节点写负载值
writeSource.setWriteCount();
writeSource.getConnection(schema,
autoCommit, handler, attachment);
rrs.setRunOnSlave(false);
rrs.setCanRunInReadDB(false);
}
}
}else{ // 强制走 master
// 默认获得的是 writeSource,也就是 走master
LOGGER.debug("rrs.getRunOnSlave() " + rrs.getRunOnSlave());
PhysicalDatasource writeSource=dbPool.getSource();
//记录写节点写负载值
writeSource.setReadCount();
writeSource.getConnection(schema, autoCommit,
handler, attachment);
rrs.setCanRunInReadDB(false);
}
}else{ // 没有 /*db_type=master/slave*/ 注解,按照原来的处理方式
LOGGER.debug("rrs.getRunOnSlave() " + rrs.getRunOnSlave()); // null
if (rrs.canRunnINReadDB(autoCommit)) {
dbPool.getRWBanlanceCon(schema,autoCommit, handler, attachment, this.database);
} else {
PhysicalDatasource writeSource =dbPool.getSource();
//记录写节点写负载值
writeSource.setWriteCount();
writeSource.getConnection(schema, autoCommit,
handler, attachment);
}
}
} else {
throw new IllegalArgumentException("Invalid DataSource:" + dbPool.getActivedIndex());
}
}
// public void getConnection(String schema,boolean autoCommit, RouteResultsetNode rrs,
// ResponseHandler handler, Object attachment) throws Exception {
// checkRequest(schema);
// if (dbPool.isInitSuccess()) {
// if (rrs.canRunnINReadDB(autoCommit)) {
// dbPool.getRWBanlanceCon(schema,autoCommit, handler, attachment,
// this.database);
// } else {
// dbPool.getSource().getConnection(schema,autoCommit, handler, attachment);
// }
//
// } else {
// throw new IllegalArgumentException("Invalid DataSource:"
// + dbPool.getActivedIndex());
// }
// }
}
@@ -0,0 +1,721 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat.backend.datasource;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReentrantLock;
import org.slf4j.Logger; import org.slf4j.LoggerFactory;
import io.mycat.MycatServer;
import io.mycat.backend.BackendConnection;
import io.mycat.backend.heartbeat.DBHeartbeat;
import io.mycat.backend.mysql.nio.handler.GetConnectionHandler;
import io.mycat.backend.mysql.nio.handler.ResponseHandler;
import io.mycat.config.Alarms;
import io.mycat.config.model.DataHostConfig;
public class PhysicalDBPool {
protected static final Logger LOGGER = LoggerFactory.getLogger(PhysicalDBPool.class);
public static final int BALANCE_NONE = 0;
public static final int BALANCE_ALL_BACK = 1;
public static final int BALANCE_ALL = 2;
public static final int BALANCE_ALL_READ = 3;
public static final int WRITE_ONLYONE_NODE = 0;
public static final int WRITE_RANDOM_NODE = 1;
public static final int WRITE_ALL_NODE = 2;
public static final long LONG_TIME = 300000;
public static final int WEIGHT = 0;
private final String hostName;
protected PhysicalDatasource[] writeSources;
protected Map<Integer, PhysicalDatasource[]> readSources;
protected volatile int activedIndex;
protected volatile boolean initSuccess;
protected final ReentrantLock switchLock = new ReentrantLock();
private final Collection<PhysicalDatasource> allDs;
private final int banlance;
private final int writeType;
private final Random random = new Random();
private final Random wnrandom = new Random();
private String[] schemas;
private final DataHostConfig dataHostConfig;
private String slaveIDs;
public PhysicalDBPool(String name, DataHostConfig conf,
PhysicalDatasource[] writeSources,
Map<Integer, PhysicalDatasource[]> readSources, int balance,
int writeType) {
this.hostName = name;
this.dataHostConfig = conf;
this.writeSources = writeSources;
this.banlance = balance;
this.writeType = writeType;
Iterator<Map.Entry<Integer, PhysicalDatasource[]>> entryItor = readSources.entrySet().iterator();
while (entryItor.hasNext()) {
PhysicalDatasource[] values = entryItor.next().getValue();
if (values.length == 0) {
entryItor.remove();
}
}
this.readSources = readSources;
this.allDs = this.genAllDataSources();
LOGGER.info("total resouces of dataHost " + this.hostName + " is :" + allDs.size());
setDataSourceProps();
}
public int getWriteType() {
return writeType;
}
private void setDataSourceProps() {
for (PhysicalDatasource ds : this.allDs) {
ds.setDbPool(this);
}
}
public PhysicalDatasource findDatasouce(BackendConnection exitsCon) {
for (PhysicalDatasource ds : this.allDs) {
if ((ds.isReadNode() == exitsCon.isFromSlaveDB())
&& ds.isMyConnection(exitsCon)) {
return ds;
}
}
LOGGER.warn("can't find connection in pool " + this.hostName + " con:" + exitsCon);
return null;
}
public String getSlaveIDs() {
return slaveIDs;
}
public void setSlaveIDs(String slaveIDs) {
this.slaveIDs = slaveIDs;
}
public String getHostName() {
return hostName;
}
/**
* all write datanodes
* @return
*/
public PhysicalDatasource[] getSources() {
return writeSources;
}
public PhysicalDatasource getSource() {
switch (writeType) {
case WRITE_ONLYONE_NODE: {
return writeSources[activedIndex];
}
case WRITE_RANDOM_NODE: {
int index = Math.abs(wnrandom.nextInt(Integer.MAX_VALUE)) % writeSources.length;
PhysicalDatasource result = writeSources[index];
if (!this.isAlive(result)) {
// find all live nodes
ArrayList<Integer> alives = new ArrayList<Integer>(writeSources.length - 1);
for (int i = 0; i < writeSources.length; i++) {
if (i != index
&& this.isAlive(writeSources[i])) {
alives.add(i);
}
}
if (alives.isEmpty()) {
result = writeSources[0];
} else {
// random select one
index = Math.abs(wnrandom.nextInt(Integer.MAX_VALUE)) % alives.size();
result = writeSources[alives.get(index)];
}
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("select write source " + result.getName()
+ " for dataHost:" + this.getHostName());
}
return result;
}
default: {
throw new java.lang.IllegalArgumentException("writeType is "
+ writeType + " ,so can't return one write datasource ");
}
}
}
public int getActivedIndex() {
return activedIndex;
}
public boolean isInitSuccess() {
return initSuccess;
}
public int next(int i) {
if (checkIndex(i)) {
return (++i == writeSources.length) ? 0 : i;
} else {
return 0;
}
}
public boolean switchSource(int newIndex, boolean isAlarm, String reason) {
if (this.writeType != PhysicalDBPool.WRITE_ONLYONE_NODE || !checkIndex(newIndex)) {
return false;
}
final ReentrantLock lock = this.switchLock;
lock.lock();
try {
int current = activedIndex;
if (current != newIndex) {
// switch index
activedIndex = newIndex;
// init again
this.init(activedIndex);
// clear all connections
this.getSources()[current].clearCons("switch datasource");
// write log
LOGGER.warn(switchMessage(current, newIndex, false, reason));
return true;
}
} finally {
lock.unlock();
}
return false;
}
private String switchMessage(int current, int newIndex, boolean alarm, String reason) {
StringBuilder s = new StringBuilder();
if (alarm) {
s.append(Alarms.DATANODE_SWITCH);
}
s.append("[Host=").append(hostName).append(",result=[").append(current).append("->");
s.append(newIndex).append("],reason=").append(reason).append(']');
return s.toString();
}
private int loop(int i) {
return i < writeSources.length ? i : (i - writeSources.length);
}
public void init(int index) {
if (!checkIndex(index)) {
index = 0;
}
int active = -1;
for (int i = 0; i < writeSources.length; i++) {
int j = loop(i + index);
if ( initSource(j, writeSources[j]) ) {
//不切换-1时,如果主写挂了 不允许切换过去
boolean isNotSwitchDs = ( dataHostConfig.getSwitchType() == DataHostConfig.NOT_SWITCH_DS );
if ( isNotSwitchDs && j > 0 ) {
break;
}
active = j;
activedIndex = active;
initSuccess = true;
LOGGER.info(getMessage(active, " init success"));
if (this.writeType == WRITE_ONLYONE_NODE) {
// only init one write datasource
MycatServer.getInstance().saveDataHostIndex(hostName, activedIndex);
break;
}
}
}
if (!checkIndex(active)) {
initSuccess = false;
StringBuilder s = new StringBuilder();
s.append(Alarms.DEFAULT).append(hostName).append(" init failure");
LOGGER.error(s.toString());
}
}
private boolean checkIndex(int i) {
return i >= 0 && i < writeSources.length;
}
private String getMessage(int index, String info) {
return new StringBuilder().append(hostName).append(" index:").append(index).append(info).toString();
}
private boolean initSource(int index, PhysicalDatasource ds) {
int initSize = ds.getConfig().getMinCon();
LOGGER.info("init backend myqsl source ,create connections total " + initSize + " for " + ds.getName() + " index :" + index);
CopyOnWriteArrayList<BackendConnection> list = new CopyOnWriteArrayList<BackendConnection>();
GetConnectionHandler getConHandler = new GetConnectionHandler(list, initSize);
// long start = System.currentTimeMillis();
// long timeOut = start + 5000 * 1000L;
for (int i = 0; i < initSize; i++) {
try {
ds.getConnection(this.schemas[i % schemas.length], true, getConHandler, null);
} catch (Exception e) {
LOGGER.warn(getMessage(index, " init connection error."), e);
}
}
long timeOut = System.currentTimeMillis() + 60 * 1000;
// waiting for finish
while (!getConHandler.finished() && (System.currentTimeMillis() < timeOut)) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
LOGGER.error("initError", e);
}
}
LOGGER.info("init result :" + getConHandler.getStatusInfo());
// for (BackendConnection c : list) {
// c.release();
// }
return !list.isEmpty();
}
public void doHeartbeat() {
if (writeSources == null || writeSources.length == 0) {
return;
}
for (PhysicalDatasource source : this.allDs) {
if (source != null) {
source.doHeartbeat();
} else {
StringBuilder s = new StringBuilder();
s.append(Alarms.DEFAULT).append(hostName).append(" current dataSource is null!");
LOGGER.error(s.toString());
}
}
}
/**
* back physical connection heartbeat check
*/
public void heartbeatCheck(long ildCheckPeriod) {
for (PhysicalDatasource ds : allDs) {
// only readnode or all write node or writetype=WRITE_ONLYONE_NODE
// and current write node will check
if (ds != null
&& (ds.getHeartbeat().getStatus() == DBHeartbeat.OK_STATUS)
&& (ds.isReadNode()
|| (this.writeType != WRITE_ONLYONE_NODE)
|| (this.writeType == WRITE_ONLYONE_NODE
&& ds == this.getSource()))) {
ds.heatBeatCheck(ds.getConfig().getIdleTimeout(), ildCheckPeriod);
}
}
}
public void startHeartbeat() {
for (PhysicalDatasource source : this.allDs) {
source.startHeartbeat();
}
}
public void stopHeartbeat() {
for (PhysicalDatasource source : this.allDs) {
source.stopHeartbeat();
}
}
/**
* 强制清除 dataSources
* @param reason
*/
public void clearDataSources(String reason) {
LOGGER.info("clear datasours of pool " + this.hostName);
for (PhysicalDatasource source : this.allDs) {
LOGGER.info("clear datasoure of pool " + this.hostName + " ds:" + source.getConfig());
source.clearCons(reason);
source.stopHeartbeat();
}
}
public Collection<PhysicalDatasource> genAllDataSources() {
LinkedList<PhysicalDatasource> allSources = new LinkedList<PhysicalDatasource>();
for (PhysicalDatasource ds : writeSources) {
if (ds != null) {
allSources.add(ds);
}
}
for (PhysicalDatasource[] dataSources : this.readSources.values()) {
for (PhysicalDatasource ds : dataSources) {
if (ds != null) {
allSources.add(ds);
}
}
}
return allSources;
}
public Collection<PhysicalDatasource> getAllDataSources() {
return this.allDs;
}
/**
* return connection for read balance
*
* @param handler
* @param attachment
* @param database
* @throws Exception
*/
public void getRWBanlanceCon(String schema, boolean autocommit,
ResponseHandler handler, Object attachment, String database) throws Exception {
PhysicalDatasource theNode = null;
ArrayList<PhysicalDatasource> okSources = null;
switch (banlance) {
case BALANCE_ALL_BACK: {
// all read nodes and the standard by masters
okSources = getAllActiveRWSources(true, false, checkSlaveSynStatus());
if (okSources.isEmpty()) {
theNode = this.getSource();
} else {
theNode = randomSelect(okSources);
}
break;
}
case BALANCE_ALL: {
okSources = getAllActiveRWSources(true, true, checkSlaveSynStatus());
theNode = randomSelect(okSources);
break;
}
case BALANCE_ALL_READ: {
okSources = getAllActiveRWSources(false, false, checkSlaveSynStatus());
theNode = randomSelect(okSources);
break;
}
case BALANCE_NONE:
default:
// return default write data source
theNode = this.getSource();
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("select read source " + theNode.getName() + " for dataHost:" + this.getHostName());
}
//统计节点读操作次数
theNode.setReadCount();
theNode.getConnection(schema, autocommit, handler, attachment);
}
/**
* slave 读负载均衡,也就是 readSource 之间实现负载均衡
* @param schema
* @param autocommit
* @param handler
* @param attachment
* @param database
* @throws Exception
*/
public void getReadBanlanceCon(String schema, boolean autocommit, ResponseHandler handler,
Object attachment, String database)throws Exception {
PhysicalDatasource theNode = null;
ArrayList<PhysicalDatasource> okSources = null;
okSources = getAllActiveRWSources(false, false, checkSlaveSynStatus());
theNode = randomSelect(okSources);
//统计节点读操作次数
theNode.setReadCount();
theNode.getConnection(schema, autocommit, handler, attachment);
}
/**
* 从 writeHost 下面的 readHost中随机获取一个 connection, 用于slave注解
* @param schema
* @param autocommit
* @param handler
* @param attachment
* @param database
* @return
* @throws Exception
*/
public boolean getReadCon(String schema, boolean autocommit, ResponseHandler handler,
Object attachment, String database)throws Exception {
PhysicalDatasource theNode = null;
LOGGER.debug("!readSources.isEmpty() " + !readSources.isEmpty());
if (!readSources.isEmpty()) {
int index = Math.abs(random.nextInt(Integer.MAX_VALUE)) % readSources.size();
PhysicalDatasource[] allSlaves = this.readSources.get(index);
// System.out.println("allSlaves.length " + allSlaves.length);
if (allSlaves != null) {
index = Math.abs(random.nextInt(Integer.MAX_VALUE)) % readSources.size();
PhysicalDatasource slave = allSlaves[index];
for (int i=0; i<allSlaves.length; i++) {
LOGGER.debug("allSlaves.length i:::::: " + i);
if (isAlive(slave)) {
if (checkSlaveSynStatus()) {
if (canSelectAsReadNode(slave)) {
theNode = slave;
break;
} else {
continue;
}
} else {
theNode = slave;
break;
}
}
// index = Math.abs(random.nextInt()) % readSources.size();
}
}
//统计节点读操作次数
if(theNode != null) {
theNode.setReadCount();
theNode.getConnection(schema, autocommit, handler, attachment);
return true;
} else {
LOGGER.warn("readhost is notavailable.");
return false;
}
}else{
LOGGER.warn("readhost is empty, readSources is empty.");
return false;
}
}
private boolean checkSlaveSynStatus() {
return ( dataHostConfig.getSlaveThreshold() != -1 )
&& (dataHostConfig.getSwitchType() == DataHostConfig.SYN_STATUS_SWITCH_DS);
}
/**
* TODO: modify by zhuam
*
* 随机选择,按权重设置随机概率。
* 在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。
* @param okSources
* @return
*/
public PhysicalDatasource randomSelect(ArrayList<PhysicalDatasource> okSources) {
if (okSources.isEmpty()) {
return this.getSource();
} else {
int length = okSources.size(); // 总个数
int totalWeight = 0; // 总权重
boolean sameWeight = true; // 权重是否都一样
for (int i = 0; i < length; i++) {
int weight = okSources.get(i).getConfig().getWeight();
totalWeight += weight; // 累计总权重
if (sameWeight && i > 0
&& weight != okSources.get(i-1).getConfig().getWeight() ) { // 计算所有权重是否一样
sameWeight = false;
}
}
if (totalWeight > 0 && !sameWeight ) {
// 如果权重不相同且权重大于0则按总权重数随机
int offset = random.nextInt(totalWeight);
// 并确定随机值落在哪个片断上
for (int i = 0; i < length; i++) {
offset -= okSources.get(i).getConfig().getWeight();
if (offset < 0) {
return okSources.get(i);
}
}
}
// 如果权重相同或权重为0则均等随机
return okSources.get( random.nextInt(length) );
//int index = Math.abs(random.nextInt()) % okSources.size();
//return okSources.get(index);
}
}
//
public int getBalance() {
return banlance;
}
private boolean isAlive(PhysicalDatasource theSource) {
return (theSource.getHeartbeat().getStatus() == DBHeartbeat.OK_STATUS);
}
private boolean canSelectAsReadNode(PhysicalDatasource theSource) {
Integer slaveBehindMaster = theSource.getHeartbeat().getSlaveBehindMaster();
int dbSynStatus = theSource.getHeartbeat().getDbSynStatus();
if ( slaveBehindMaster == null || dbSynStatus == DBHeartbeat.DB_SYN_ERROR) {
return false;
}
boolean isSync = dbSynStatus == DBHeartbeat.DB_SYN_NORMAL;
boolean isNotDelay = slaveBehindMaster < this.dataHostConfig.getSlaveThreshold();
return isSync && isNotDelay;
}
/**
* return all backup write sources
*
* @param includeWriteNode if include write nodes
* @param includeCurWriteNode if include current active write node. invalid when <code>includeWriteNode<code> is false
* @param filterWithSlaveThreshold
*
* @return
*/
private ArrayList<PhysicalDatasource> getAllActiveRWSources(
boolean includeWriteNode, boolean includeCurWriteNode, boolean filterWithSlaveThreshold) {
int curActive = activedIndex;
ArrayList<PhysicalDatasource> okSources = new ArrayList<PhysicalDatasource>(this.allDs.size());
for (int i = 0; i < this.writeSources.length; i++) {
PhysicalDatasource theSource = writeSources[i];
if (isAlive(theSource)) {// write node is active
if (includeWriteNode) {
boolean isCurWriteNode = ( i == curActive );
if ( isCurWriteNode && includeCurWriteNode == false) {
// not include cur active source
} else if (filterWithSlaveThreshold && theSource.isSalveOrRead() ) {
boolean selected = canSelectAsReadNode(theSource);
if ( selected ) {
okSources.add(theSource);
} else {
continue;
}
} else {
okSources.add(theSource);
}
}
if (!readSources.isEmpty()) {
// check all slave nodes
PhysicalDatasource[] allSlaves = this.readSources.get(i);
if (allSlaves != null) {
for (PhysicalDatasource slave : allSlaves) {
if (isAlive(slave)) {
if (filterWithSlaveThreshold) {
boolean selected = canSelectAsReadNode(slave);
if ( selected ) {
okSources.add(slave);
} else {
continue;
}
} else {
okSources.add(slave);
}
}
}
}
}
} else {
// TODO : add by zhuam
// 如果写节点不OK, 也要保证临时的读服务正常
if ( this.dataHostConfig.isTempReadHostAvailable()
&& !readSources.isEmpty()) {
// check all slave nodes
PhysicalDatasource[] allSlaves = this.readSources.get(i);
if (allSlaves != null) {
for (PhysicalDatasource slave : allSlaves) {
if (isAlive(slave)) {
if (filterWithSlaveThreshold) {
if (canSelectAsReadNode(slave)) {
okSources.add(slave);
} else {
continue;
}
} else {
okSources.add(slave);
}
}
}
}
}
}
}
return okSources;
}
public String[] getSchemas() {
return schemas;
}
public void setSchemas(String[] mySchemas) {
this.schemas = mySchemas;
}
}
@@ -0,0 +1,481 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat.backend.datasource;
import io.mycat.MycatServer;
import io.mycat.backend.BackendConnection;
import io.mycat.backend.ConMap;
import io.mycat.backend.ConQueue;
import io.mycat.backend.heartbeat.DBHeartbeat;
import io.mycat.backend.mysql.nio.MySQLConnection;
import io.mycat.backend.mysql.nio.handler.ConnectionHeartBeatHandler;
import io.mycat.backend.mysql.nio.handler.DelegateResponseHandler;
import io.mycat.backend.mysql.nio.handler.NewConnectionRespHandler;
import io.mycat.backend.mysql.nio.handler.ResponseHandler;
import io.mycat.config.Alarms;
import io.mycat.config.model.DBHostConfig;
import io.mycat.config.model.DataHostConfig;
import io.mycat.util.TimeUtil;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicLong;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class PhysicalDatasource {
private static final Logger LOGGER = LoggerFactory.getLogger(PhysicalDatasource.class);
private final String name;
private final int size;
private final DBHostConfig config;
private final ConMap conMap = new ConMap();
private DBHeartbeat heartbeat;
private final boolean readNode;
private volatile long heartbeatRecoveryTime;
private final DataHostConfig hostConfig;
private final ConnectionHeartBeatHandler conHeartBeatHanler = new ConnectionHeartBeatHandler();
private PhysicalDBPool dbPool;
// 添加DataSource读计数
private AtomicLong readCount = new AtomicLong(0);
// 添加DataSource写计数
private AtomicLong writeCount = new AtomicLong(0);
public PhysicalDatasource(DBHostConfig config, DataHostConfig hostConfig,
boolean isReadNode) {
this.size = config.getMaxCon();
this.config = config;
this.name = config.getHostName();
this.hostConfig = hostConfig;
heartbeat = this.createHeartBeat();
this.readNode = isReadNode;
}
public boolean isMyConnection(BackendConnection con) {
if (con instanceof MySQLConnection) {
return ((MySQLConnection) con).getPool() == this;
} else {
return false;
}
}
public long getReadCount() {
return readCount.get();
}
public void setReadCount() {
readCount.addAndGet(1);
}
public long getWriteCount() {
return writeCount.get();
}
public void setWriteCount() {
writeCount.addAndGet(1);
}
public DataHostConfig getHostConfig() {
return hostConfig;
}
public boolean isReadNode() {
return readNode;
}
public int getSize() {
return size;
}
public void setDbPool(PhysicalDBPool dbPool) {
this.dbPool = dbPool;
}
public PhysicalDBPool getDbPool() {
return dbPool;
}
public abstract DBHeartbeat createHeartBeat();
public String getName() {
return name;
}
public long getExecuteCount() {
long executeCount = 0;
for (ConQueue queue : conMap.getAllConQueue()) {
executeCount += queue.getExecuteCount();
}
return executeCount;
}
public long getExecuteCountForSchema(String schema) {
return conMap.getSchemaConQueue(schema).getExecuteCount();
}
public int getActiveCountForSchema(String schema) {
return conMap.getActiveCountForSchema(schema, this);
}
public int getIdleCountForSchema(String schema) {
ConQueue queue = conMap.getSchemaConQueue(schema);
int total = 0;
total += queue.getAutoCommitCons().size()
+ queue.getManCommitCons().size();
return total;
}
public DBHeartbeat getHeartbeat() {
return heartbeat;
}
public int getIdleCount() {
int total = 0;
for (ConQueue queue : conMap.getAllConQueue()) {
total += queue.getAutoCommitCons().size()
+ queue.getManCommitCons().size();
}
return total;
}
private boolean validSchema(String schema) {
String theSchema = schema;
return theSchema != null && !"".equals(theSchema)
&& !"snyn...".equals(theSchema);
}
private void checkIfNeedHeartBeat(
LinkedList<BackendConnection> heartBeatCons, ConQueue queue,
ConcurrentLinkedQueue<BackendConnection> checkLis,
long hearBeatTime, long hearBeatTime2) {
int maxConsInOneCheck = 10;
Iterator<BackendConnection> checkListItor = checkLis.iterator();
while (checkListItor.hasNext()) {
BackendConnection con = checkListItor.next();
if (con.isClosedOrQuit()) {
checkListItor.remove();
continue;
}
if (validSchema(con.getSchema())) {
if (con.getLastTime() < hearBeatTime
&& heartBeatCons.size() < maxConsInOneCheck) {
checkListItor.remove();
// Heart beat check
con.setBorrowed(true);
heartBeatCons.add(con);
}
} else if (con.getLastTime() < hearBeatTime2) {
// not valid schema conntion should close for idle
// exceed 2*conHeartBeatPeriod
checkListItor.remove();
con.close(" heart beate idle ");
}
}
}
public int getIndex() {
int currentIndex = 0;
for (int i = 0; i < dbPool.getSources().length; i++) {
PhysicalDatasource writeHostDatasource = dbPool.getSources()[i];
if (writeHostDatasource.getName().equals(getName())) {
currentIndex = i;
break;
}
}
return currentIndex;
}
public boolean isSalveOrRead() {
int currentIndex = getIndex();
if (currentIndex != dbPool.activedIndex || this.readNode) {
return true;
}
return false;
}
public void heatBeatCheck(long timeout, long conHeartBeatPeriod) {
// int ildeCloseCount = hostConfig.getMinCon() * 3;
int maxConsInOneCheck = 5;
LinkedList<BackendConnection> heartBeatCons = new LinkedList<BackendConnection>();
long hearBeatTime = TimeUtil.currentTimeMillis() - conHeartBeatPeriod;
long hearBeatTime2 = TimeUtil.currentTimeMillis() - 2
* conHeartBeatPeriod;
for (ConQueue queue : conMap.getAllConQueue()) {
checkIfNeedHeartBeat(heartBeatCons, queue,
queue.getAutoCommitCons(), hearBeatTime, hearBeatTime2);
if (heartBeatCons.size() < maxConsInOneCheck) {
checkIfNeedHeartBeat(heartBeatCons, queue,
queue.getManCommitCons(), hearBeatTime, hearBeatTime2);
} else if (heartBeatCons.size() >= maxConsInOneCheck) {
break;
}
}
if (!heartBeatCons.isEmpty()) {
for (BackendConnection con : heartBeatCons) {
conHeartBeatHanler
.doHeartBeat(con, hostConfig.getHearbeatSQL());
}
}
// check if there has timeouted heatbeat cons
conHeartBeatHanler.abandTimeOuttedConns();
int idleCons = getIdleCount();
int activeCons = this.getActiveCount();
int createCount = (hostConfig.getMinCon() - idleCons) / 3;
// create if idle too little
if ((createCount > 0) && (idleCons + activeCons < size)
&& (idleCons < hostConfig.getMinCon())) {
createByIdleLitte(idleCons, createCount);
} else if (idleCons > hostConfig.getMinCon()) {
closeByIdleMany(idleCons - hostConfig.getMinCon());
} else {
int activeCount = this.getActiveCount();
if (activeCount > size) {
StringBuilder s = new StringBuilder();
s.append(Alarms.DEFAULT).append("DATASOURCE EXCEED [name=")
.append(name).append(",active=");
s.append(activeCount).append(",size=").append(size).append(']');
LOGGER.warn(s.toString());
}
}
}
private void closeByIdleMany(int ildeCloseCount) {
LOGGER.info("too many ilde cons ,close some for datasouce " + name);
List<BackendConnection> readyCloseCons = new ArrayList<BackendConnection>(
ildeCloseCount);
for (ConQueue queue : conMap.getAllConQueue()) {
readyCloseCons.addAll(queue.getIdleConsToClose(ildeCloseCount));
if (readyCloseCons.size() >= ildeCloseCount) {
break;
}
}
for (BackendConnection idleCon : readyCloseCons) {
if (idleCon.isBorrowed()) {
LOGGER.warn("find idle con is using " + idleCon);
}
idleCon.close("too many idle con");
}
}
private void createByIdleLitte(int idleCons, int createCount) {
LOGGER.info("create connections ,because idle connection not enough ,cur is "
+ idleCons
+ ", minCon is "
+ hostConfig.getMinCon()
+ " for "
+ name);
NewConnectionRespHandler simpleHandler = new NewConnectionRespHandler();
final String[] schemas = dbPool.getSchemas();
for (int i = 0; i < createCount; i++) {
if (this.getActiveCount() + this.getIdleCount() >= size) {
break;
}
try {
// creat new connection
this.createNewConnection(simpleHandler, null, schemas[i
% schemas.length]);
} catch (IOException e) {
LOGGER.warn("create connection err " + e);
}
}
}
public int getActiveCount() {
return this.conMap.getActiveCountForDs(this);
}
public void clearCons(String reason) {
this.conMap.clearConnections(reason, this);
}
public void startHeartbeat() {
heartbeat.start();
}
public void stopHeartbeat() {
heartbeat.stop();
}
public void doHeartbeat() {
// 未到预定恢复时间,不执行心跳检测。
if (TimeUtil.currentTimeMillis() < heartbeatRecoveryTime) {
return;
}
if (!heartbeat.isStop()) {
try {
heartbeat.heartbeat();
} catch (Exception e) {
LOGGER.error(name + " heartbeat error.", e);
}
}
}
private BackendConnection takeCon(BackendConnection conn,
final ResponseHandler handler, final Object attachment,
String schema) {
conn.setBorrowed(true);
if (!conn.getSchema().equals(schema)) {
// need do schema syn in before sql send
conn.setSchema(schema);
}
ConQueue queue = conMap.getSchemaConQueue(schema);
queue.incExecuteCount();
conn.setAttachment(attachment);
conn.setLastTime(System.currentTimeMillis()); // 每次取连接的时候,更新下lasttime,防止在前端连接检查的时候,关闭连接,导致sql执行失败
handler.connectionAcquired(conn);
return conn;
}
private void createNewConnection(final ResponseHandler handler,
final Object attachment, final String schema) throws IOException {
// aysn create connection
MycatServer.getInstance().getBusinessExecutor().execute(new Runnable() {
public void run() {
try {
createNewConnection(new DelegateResponseHandler(handler) {
@Override
public void connectionError(Throwable e, BackendConnection conn) {
handler.connectionError(e, conn);
}
@Override
public void connectionAcquired(BackendConnection conn) {
takeCon(conn, handler, attachment, schema);
}
}, schema);
} catch (IOException e) {
handler.connectionError(e, null);
}
}
});
}
public void getConnection(String schema, boolean autocommit,
final ResponseHandler handler, final Object attachment)
throws IOException {
// 从当前连接map中拿取已建立好的后端连接
BackendConnection con = this.conMap.tryTakeCon(schema, autocommit);
if (con != null) {
//如果不为空,则绑定对应前端请求的handler
takeCon(con, handler, attachment, schema);
return;
} else {
int activeCons = this.getActiveCount();// 当前最大活动连接
if (activeCons + 1 > size) {// 下一个连接大于最大连接数
LOGGER.error("the max activeConnnections size can not be max than maxconnections");
throw new IOException("the max activeConnnections size can not be max than maxconnections");
} else { // create connection
LOGGER.info("no ilde connection in pool,create new connection for " + this.name + " of schema " + schema);
createNewConnection(handler, attachment, schema);
}
}
}
private void returnCon(BackendConnection c) {
c.setAttachment(null);
c.setBorrowed(false);
c.setLastTime(TimeUtil.currentTimeMillis());
ConQueue queue = this.conMap.getSchemaConQueue(c.getSchema());
boolean ok = false;
if (c.isAutocommit()) {
ok = queue.getAutoCommitCons().offer(c);
} else {
ok = queue.getManCommitCons().offer(c);
}
if (!ok) {
LOGGER.warn("can't return to pool ,so close con " + c);
c.close("can't return to pool ");
}
}
public void releaseChannel(BackendConnection c) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("release channel " + c);
}
// release connection
returnCon(c);
}
public void connectionClosed(BackendConnection conn) {
ConQueue queue = this.conMap.getSchemaConQueue(conn.getSchema());
if (queue != null) {
queue.removeCon(conn);
}
}
/**
* 创建新连接
*/
public abstract void createNewConnection(ResponseHandler handler, String schema) throws IOException;
/**
* 测试连接,用于初始化及热更新配置检测
*/
public abstract boolean testConnection(String schema) throws IOException;
public long getHeartbeatRecoveryTime() {
return heartbeatRecoveryTime;
}
public void setHeartbeatRecoveryTime(long heartbeatRecoveryTime) {
this.heartbeatRecoveryTime = heartbeatRecoveryTime;
}
public DBHostConfig getConfig() {
return config;
}
public boolean isAlive() {
return getHeartbeat().getStatus() == DBHeartbeat.OK_STATUS;
}
}
@@ -0,0 +1,133 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat.backend.heartbeat;
import java.util.concurrent.atomic.AtomicBoolean;
import io.mycat.statistic.DataSourceSyncRecorder;
import io.mycat.statistic.HeartbeatRecorder;
public abstract class DBHeartbeat {
public static final int DB_SYN_ERROR = -1;
public static final int DB_SYN_NORMAL = 1;
public static final int OK_STATUS = 1;
public static final int ERROR_STATUS = -1;
public static final int TIMEOUT_STATUS = -2;
public static final int INIT_STATUS = 0;
private static final long DEFAULT_HEARTBEAT_TIMEOUT = 30 * 1000L;
private static final int DEFAULT_HEARTBEAT_RETRY = 10;
// heartbeat config
protected long heartbeatTimeout = DEFAULT_HEARTBEAT_TIMEOUT; // 心跳超时时间
protected int heartbeatRetry = DEFAULT_HEARTBEAT_RETRY; // 检查连接发生异常到切换,重试次数
protected String heartbeatSQL;// 静态心跳语句
protected final AtomicBoolean isStop = new AtomicBoolean(true);
protected final AtomicBoolean isChecking = new AtomicBoolean(false);
protected int errorCount;
protected volatile int status;
protected final HeartbeatRecorder recorder = new HeartbeatRecorder();
protected final DataSourceSyncRecorder asynRecorder = new DataSourceSyncRecorder();
private volatile Integer slaveBehindMaster;
private volatile int dbSynStatus = DB_SYN_NORMAL;
public Integer getSlaveBehindMaster() {
return slaveBehindMaster;
}
public int getDbSynStatus() {
return dbSynStatus;
}
public void setDbSynStatus(int dbSynStatus) {
this.dbSynStatus = dbSynStatus;
}
public void setSlaveBehindMaster(Integer slaveBehindMaster) {
this.slaveBehindMaster = slaveBehindMaster;
}
public int getStatus() {
return status;
}
public boolean isChecking() {
return isChecking.get();
}
public abstract void start();
public abstract void stop();
public boolean isStop() {
return isStop.get();
}
public int getErrorCount() {
return errorCount;
}
public HeartbeatRecorder getRecorder() {
return recorder;
}
public abstract String getLastActiveTime();
public abstract long getTimeout();
public abstract void heartbeat();
public long getHeartbeatTimeout() {
return heartbeatTimeout;
}
public void setHeartbeatTimeout(long heartbeatTimeout) {
this.heartbeatTimeout = heartbeatTimeout;
}
public int getHeartbeatRetry() {
return heartbeatRetry;
}
public void setHeartbeatRetry(int heartbeatRetry) {
this.heartbeatRetry = heartbeatRetry;
}
public String getHeartbeatSQL() {
return heartbeatSQL;
}
public void setHeartbeatSQL(String heartbeatSQL) {
this.heartbeatSQL = heartbeatSQL;
}
public boolean isNeedHeartbeat() {
return heartbeatSQL != null;
}
public DataSourceSyncRecorder getAsynRecorder() {
return this.asynRecorder;
}
}
@@ -0,0 +1,200 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat.backend.heartbeat;
import io.mycat.backend.mysql.nio.MySQLDataSource;
import io.mycat.server.interceptor.impl.GlobalTableUtil;
import io.mycat.sqlengine.OneRawSQLQueryResultHandler;
import io.mycat.sqlengine.SQLJob;
import io.mycat.sqlengine.SQLQueryResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.fastjson.JSON;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author digdeep@126.com
*/
public class MySQLConsistencyChecker{
public static final Logger LOGGER = LoggerFactory.getLogger(MySQLConsistencyChecker.class);
private final MySQLDataSource source;
private final ReentrantLock lock;
private AtomicInteger jobCount = new AtomicInteger();
private String countSQL;
private String maxSQL;
private String tableName; // global table name
private long beginTime;
// private String columnExistSQL = "select count(*) as "+GlobalTableUtil.INNER_COLUMN
// + " from information_schema.columns where column_name='"
// + GlobalTableUtil.GLOBAL_TABLE_MYCAT_COLUMN + "' and table_name='";
// 此处用到了 mysql 多行转一行 group_concat 的用法,主要是为了简化对结果的处理
// 得到的结果类似于:id,name,_mycat_op_time
private String columnExistSQL = "select group_concat(COLUMN_NAME separator ',') as "
+ GlobalTableUtil.INNER_COLUMN +" from information_schema.columns where TABLE_NAME='"; //user' and TABLE_SCHEMA='db1';
private List<SQLQueryResult<Map<String, String>>> list = new ArrayList<>();
public MySQLConsistencyChecker(MySQLDataSource source, String tableName) {
this.source = source;
this.lock = new ReentrantLock(false);
this.tableName = tableName;
this.countSQL = " select count(*) as "+GlobalTableUtil.COUNT_COLUMN+" from "
+ this.tableName;
this.maxSQL = " select max("+GlobalTableUtil.GLOBAL_TABLE_MYCAT_COLUMN+") as "+
GlobalTableUtil.MAX_COLUMN+" from " + this.tableName;
this.columnExistSQL += this.tableName +"' ";
}
public void checkRecordCout() {
// ["db3","db2","db1"]
lock.lock();
try{
this.jobCount.set(0);
beginTime = new Date().getTime();
String[] physicalSchemas = source.getDbPool().getSchemas();
for(String dbName : physicalSchemas){
MySQLConsistencyHelper detector = new MySQLConsistencyHelper(this, null);
OneRawSQLQueryResultHandler resultHandler =
new OneRawSQLQueryResultHandler(new String[] {GlobalTableUtil.COUNT_COLUMN}, detector);
SQLJob sqlJob = new SQLJob(this.getCountSQL(), dbName, resultHandler, source);
detector.setSqlJob(sqlJob);
sqlJob.run();
this.jobCount.incrementAndGet();
}
}finally{
lock.unlock();
}
}
public void checkMaxTimeStamp() {
// ["db3","db2","db1"]
lock.lock();
try{
this.jobCount.set(0);
beginTime = new Date().getTime();
String[] physicalSchemas = source.getDbPool().getSchemas();
for(String dbName : physicalSchemas){
MySQLConsistencyHelper detector = new MySQLConsistencyHelper(this, null);
OneRawSQLQueryResultHandler resultHandler =
new OneRawSQLQueryResultHandler(new String[] {GlobalTableUtil.MAX_COLUMN}, detector);
SQLJob sqlJob = new SQLJob(this.getMaxSQL(), dbName, resultHandler, source);
detector.setSqlJob(sqlJob);
sqlJob.run();
this.jobCount.incrementAndGet();
}
}finally{
lock.unlock();
}
}
/**
* check inner column exist or not
*/
public void checkInnerColumnExist() {
// ["db3","db2","db1"]
lock.lock();
try{
this.jobCount.set(0);
beginTime = new Date().getTime();
String[] physicalSchemas = source.getDbPool().getSchemas();
for(String dbName : physicalSchemas){
MySQLConsistencyHelper detector = new MySQLConsistencyHelper(this, null, 1);
OneRawSQLQueryResultHandler resultHandler =
new OneRawSQLQueryResultHandler(new String[] {GlobalTableUtil.INNER_COLUMN}, detector);
String db = " and table_schema='" + dbName + "'";
SQLJob sqlJob = new SQLJob(this.columnExistSQL + db , dbName, resultHandler, source);
detector.setSqlJob(sqlJob);//table_schema='db1'
LOGGER.debug(sqlJob.toString());
sqlJob.run();
this.jobCount.incrementAndGet();
}
}finally{
lock.unlock();
}
}
public void setResult(SQLQueryResult<Map<String, String>> result) {
// LOGGER.debug("setResult::::::::::" + JSON.toJSONString(result));
lock.lock();
try{
this.jobCount.decrementAndGet();
if(result != null && result.isSuccess()){
result.setTableName(tableName);
list.add(result);
}else{
if(result != null && result.getResult() != null){
String sql = null;
if(result.getResult().containsKey(GlobalTableUtil.COUNT_COLUMN))
sql = this.getCountSQL();
if(result.getResult().containsKey(GlobalTableUtil.MAX_COLUMN))
sql = this.getMaxSQL();
if(result.getResult().containsKey(GlobalTableUtil.INNER_COLUMN))
sql = this.getColumnExistSQL();
LOGGER.warn(sql+ " execute failed in db: " + result.getDataNode()
+ " during global table consistency check task.");
}
}
if(this.jobCount.get() <= 0 || isTimeOut()){
GlobalTableUtil.finished(list);
}
}finally{
lock.unlock();
}
}
public boolean isTimeOut(){
long duration = new Date().getTime() - this.beginTime;
return TimeUnit.MINUTES.convert(duration, TimeUnit.MILLISECONDS) > 1; // 1分钟超时
}
public String getCountSQL() {
return countSQL;
}
public String getColumnExistSQL() {
return columnExistSQL;
}
public void setColumnExistSQL(String columnExistSQL) {
this.columnExistSQL = columnExistSQL;
}
public String getMaxSQL() {
return maxSQL;
}
public String getTableName() {
return tableName;
}
public MySQLDataSource getSource() {
return source;
}
}
@@ -0,0 +1,132 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat.backend.heartbeat;
import io.mycat.server.interceptor.impl.GlobalTableUtil;
import io.mycat.sqlengine.SQLJob;
import io.mycat.sqlengine.SQLQueryResult;
import io.mycat.sqlengine.SQLQueryResultListener;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.fastjson.JSON;
/**
* @author digdeep@126.com
*/
public class MySQLConsistencyHelper implements SQLQueryResultListener<SQLQueryResult<Map<String, String>>> {
private static final Logger LOGGER = LoggerFactory.getLogger(MySQLConsistencyHelper.class);
private MySQLConsistencyChecker heartbeat;
private volatile SQLJob sqlJob;
private int RETRY_TIMES = 5;
private AtomicInteger retryTime = new AtomicInteger();
public MySQLConsistencyHelper(MySQLConsistencyChecker heartbeat, SQLJob sqlJob) {
this.heartbeat = heartbeat;
this.sqlJob = sqlJob;
this.retryTime.set(RETRY_TIMES);
}
public MySQLConsistencyHelper(MySQLConsistencyChecker heartbeat,
SQLJob sqlJob, int retryTime) {
this.heartbeat = heartbeat;
this.sqlJob = sqlJob;
if(retryTime > 0 && retryTime < 10)
this.retryTime.set(retryTime);
else
this.retryTime.set(RETRY_TIMES);
}
@Override
public void onResult(SQLQueryResult<Map<String, String>> result) {
// {"dataNode":"db2","result":{"max_timestamp":"1450423751170"},"success":true}
// {"dataNode":"db3","result":{"count(*)":"1"},"success":true}
LOGGER.debug("resultresultresultresult:" + JSON.toJSONString(result));
Map<String, String> rowMap = null;
String count = null; String innerCol = null;
String maxTimestamp = null;
if(result != null)
rowMap = result.getResult();
if(rowMap != null){
maxTimestamp = rowMap.get(GlobalTableUtil.MAX_COLUMN);
count = rowMap.get(GlobalTableUtil.COUNT_COLUMN);
innerCol = rowMap.get(GlobalTableUtil.INNER_COLUMN);
if((rowMap.containsKey(GlobalTableUtil.MAX_COLUMN) && StringUtils.isNotBlank(maxTimestamp))
|| (rowMap.containsKey(GlobalTableUtil.COUNT_COLUMN) && StringUtils.isNotBlank(count))
|| (rowMap.containsKey(GlobalTableUtil.INNER_COLUMN) && StringUtils.isNotBlank(innerCol))){
heartbeat.setResult(result);
return;
}else{
if(this.retryTime.get() > 0){
try {
TimeUnit.MICROSECONDS.sleep(10);
} catch (InterruptedException e) {
}
this.retryTime.decrementAndGet();
this.sqlJob.run();
return;
}
heartbeat.setResult(result);
return;
}
}else{
if(this.retryTime.get() > 0){
try {
TimeUnit.MICROSECONDS.sleep(3);
} catch (InterruptedException e) {
}
this.retryTime.decrementAndGet();
this.sqlJob.run();
return;
}
heartbeat.setResult(result);
return;
}
}
public void close(String msg) {
SQLJob curJob = sqlJob;
if (curJob != null && !curJob.isFinished()) {
curJob.teminate(msg);
sqlJob = null;
}
}
public MySQLConsistencyChecker getHeartbeat() {
return heartbeat;
}
public SQLJob getSqlJob() {
return sqlJob;
}
public void setSqlJob(SQLJob sqlJob) {
this.sqlJob = sqlJob;
}
}
@@ -0,0 +1,216 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat.backend.heartbeat;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import io.mycat.backend.datasource.PhysicalDBPool;
import io.mycat.backend.datasource.PhysicalDatasource;
import io.mycat.backend.mysql.nio.MySQLDataSource;
import io.mycat.config.model.DataHostConfig;
import io.mycat.sqlengine.OneRawSQLQueryResultHandler;
import io.mycat.sqlengine.SQLJob;
import io.mycat.sqlengine.SQLQueryResult;
import io.mycat.sqlengine.SQLQueryResultListener;
import io.mycat.util.TimeUtil;
/**
* @author mycat
*/
public class MySQLDetector implements SQLQueryResultListener<SQLQueryResult<Map<String, String>>> {
private MySQLHeartbeat heartbeat;
private long heartbeatTimeout;
private final AtomicBoolean isQuit;
private volatile long lastSendQryTime;
private volatile long lasstReveivedQryTime;
private volatile SQLJob sqlJob;
private static final String[] MYSQL_SLAVE_STAUTS_COLMS = new String[] {
"Seconds_Behind_Master",
"Slave_IO_Running",
"Slave_SQL_Running",
"Slave_IO_State",
"Master_Host",
"Master_User",
"Master_Port",
"Connect_Retry",
"Last_IO_Error"};
private static final String[] MYSQL_CLUSTER_STAUTS_COLMS = new String[] {
"Variable_name",
"Value"};
public MySQLDetector(MySQLHeartbeat heartbeat) {
this.heartbeat = heartbeat;
this.isQuit = new AtomicBoolean(false);
}
public MySQLHeartbeat getHeartbeat() {
return heartbeat;
}
public long getHeartbeatTimeout() {
return heartbeatTimeout;
}
public void setHeartbeatTimeout(long heartbeatTimeout) {
this.heartbeatTimeout = heartbeatTimeout;
}
public boolean isHeartbeatTimeout() {
return TimeUtil.currentTimeMillis() > Math.max(lastSendQryTime,
lasstReveivedQryTime) + heartbeatTimeout;
}
public long getLastSendQryTime() {
return lastSendQryTime;
}
public long getLasstReveivedQryTime() {
return lasstReveivedQryTime;
}
public void heartbeat() {
lastSendQryTime = System.currentTimeMillis();
MySQLDataSource ds = heartbeat.getSource();
String databaseName = ds.getDbPool().getSchemas()[0];
String[] fetchColms={};
if (heartbeat.getSource().getHostConfig().isShowSlaveSql() ) {
fetchColms=MYSQL_SLAVE_STAUTS_COLMS;
}
if (heartbeat.getSource().getHostConfig().isShowClusterSql() ) {
fetchColms=MYSQL_CLUSTER_STAUTS_COLMS;
}
OneRawSQLQueryResultHandler resultHandler = new OneRawSQLQueryResultHandler( fetchColms, this);
sqlJob = new SQLJob(heartbeat.getHeartbeatSQL(), databaseName, resultHandler, ds);
sqlJob.run();
}
public void quit() {
if (isQuit.compareAndSet(false, true)) {
close("heart beat quit");
}
}
public boolean isQuit() {
return isQuit.get();
}
@Override
public void onResult(SQLQueryResult<Map<String, String>> result) {
if (result.isSuccess()) {
int balance = heartbeat.getSource().getDbPool().getBalance();
PhysicalDatasource source = heartbeat.getSource();
int switchType = source.getHostConfig().getSwitchType();
Map<String, String> resultResult = result.getResult();
if ( switchType == DataHostConfig.SYN_STATUS_SWITCH_DS
&& source.getHostConfig().isShowSlaveSql()) {
String Slave_IO_Running = resultResult != null ? resultResult.get("Slave_IO_Running") : null;
String Slave_SQL_Running = resultResult != null ? resultResult.get("Slave_SQL_Running") : null;
if (Slave_IO_Running != null
&& Slave_IO_Running.equals(Slave_SQL_Running)
&& Slave_SQL_Running.equals("Yes")) {
heartbeat.setDbSynStatus(DBHeartbeat.DB_SYN_NORMAL);
String Seconds_Behind_Master = resultResult.get( "Seconds_Behind_Master");
if (null != Seconds_Behind_Master && !"".equals(Seconds_Behind_Master)) {
int Behind_Master = Integer.parseInt(Seconds_Behind_Master);
if ( Behind_Master > source.getHostConfig().getSlaveThreshold() ) {
MySQLHeartbeat.LOGGER.warn("found MySQL master/slave Replication delay !!! "
+ heartbeat.getSource().getConfig() + ", binlog sync time delay: " + Behind_Master + "s" );
}
heartbeat.setSlaveBehindMaster( Behind_Master );
}
} else if( source.isSalveOrRead() ) {
//String Last_IO_Error = resultResult != null ? resultResult.get("Last_IO_Error") : null;
MySQLHeartbeat.LOGGER.warn("found MySQL master/slave Replication err !!! "
+ heartbeat.getSource().getConfig() + ", " + resultResult);
heartbeat.setDbSynStatus(DBHeartbeat.DB_SYN_ERROR);
}
heartbeat.getAsynRecorder().set(resultResult, switchType);
heartbeat.setResult(MySQLHeartbeat.OK_STATUS, this, null);
} else if ( switchType==DataHostConfig.CLUSTER_STATUS_SWITCH_DS
&& source.getHostConfig().isShowClusterSql() ) {
//String Variable_name = resultResult != null ? resultResult.get("Variable_name") : null;
String wsrep_cluster_status = resultResult != null ? resultResult.get("wsrep_cluster_status") : null;// Primary
String wsrep_connected = resultResult != null ? resultResult.get("wsrep_connected") : null;// ON
String wsrep_ready = resultResult != null ? resultResult.get("wsrep_ready") : null;// ON
if ("ON".equals(wsrep_connected)
&& "ON".equals(wsrep_ready)
&& "Primary".equals(wsrep_cluster_status)) {
heartbeat.setDbSynStatus(DBHeartbeat.DB_SYN_NORMAL);
heartbeat.setResult(MySQLHeartbeat.OK_STATUS, this, null);
} else {
MySQLHeartbeat.LOGGER.warn("found MySQL cluster status err !!! "
+ heartbeat.getSource().getConfig()
+ " wsrep_cluster_status: "+ wsrep_cluster_status
+ " wsrep_connected: "+ wsrep_connected
+ " wsrep_ready: "+ wsrep_ready
);
heartbeat.setDbSynStatus(DBHeartbeat.DB_SYN_ERROR);
heartbeat.setResult(MySQLHeartbeat.ERROR_STATUS, this, null);
}
heartbeat.getAsynRecorder().set(resultResult, switchType);
} else {
heartbeat.setResult(MySQLHeartbeat.OK_STATUS, this, null);
}
//监测数据库同步状态,在 switchType=-1或者1的情况下,也需要收集主从同步状态
heartbeat.getAsynRecorder().set(resultResult, switchType);
} else {
heartbeat.setResult(MySQLHeartbeat.ERROR_STATUS, this, null);
}
lasstReveivedQryTime = System.currentTimeMillis();
heartbeat.getRecorder().set((lasstReveivedQryTime - lastSendQryTime));
}
public void close(String msg) {
SQLJob curJob = sqlJob;
if (curJob != null && !curJob.isFinished()) {
curJob.teminate(msg);
sqlJob = null;
}
}
}
@@ -0,0 +1,277 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat.backend.heartbeat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.locks.ReentrantLock;
import org.slf4j.Logger; import org.slf4j.LoggerFactory;
import io.mycat.backend.datasource.PhysicalDBPool;
import io.mycat.backend.datasource.PhysicalDatasource;
import io.mycat.backend.mysql.nio.MySQLDataSource;
import io.mycat.config.model.DataHostConfig;
/**
* @author mycat
*/
public class MySQLHeartbeat extends DBHeartbeat {
private static final int MAX_RETRY_COUNT = 5;
public static final Logger LOGGER = LoggerFactory.getLogger(MySQLHeartbeat.class);
private final MySQLDataSource source;
private final ReentrantLock lock;
private final int maxRetryCount;
private MySQLDetector detector;
public MySQLHeartbeat(MySQLDataSource source) {
this.source = source;
this.lock = new ReentrantLock(false);
this.maxRetryCount = MAX_RETRY_COUNT;
this.status = INIT_STATUS;
this.heartbeatSQL = source.getHostConfig().getHearbeatSQL();
}
public MySQLDataSource getSource() {
return source;
}
public MySQLDetector getDetector() {
return detector;
}
public long getTimeout() {
MySQLDetector detector = this.detector;
if (detector == null) {
return -1L;
}
return detector.getHeartbeatTimeout();
}
public String getLastActiveTime() {
MySQLDetector detector = this.detector;
if (detector == null) {
return null;
}
long t = detector.getLasstReveivedQryTime();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(new Date(t));
}
public void start() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
isStop.compareAndSet(true, false);
super.status = DBHeartbeat.OK_STATUS;
} finally {
lock.unlock();
}
}
public void stop() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (isStop.compareAndSet(false, true)) {
if (isChecking.get()) {
// nothing
} else {
MySQLDetector detector = this.detector;
if (detector != null) {
detector.quit();
isChecking.set(false);
}
}
}
} finally {
lock.unlock();
}
}
/**
* execute heart beat
*/
public void heartbeat() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (isChecking.compareAndSet(false, true)) {
MySQLDetector detector = this.detector;
if (detector == null || detector.isQuit()) {
try {
detector = new MySQLDetector(this);
detector.heartbeat();
} catch (Exception e) {
LOGGER.warn(source.getConfig().toString(), e);
setResult(ERROR_STATUS, detector, null);
return;
}
this.detector = detector;
} else {
detector.heartbeat();
}
} else {
MySQLDetector detector = this.detector;
if (detector != null) {
if (detector.isQuit()) {
isChecking.compareAndSet(true, false);
} else if (detector.isHeartbeatTimeout()) {
setResult(TIMEOUT_STATUS, detector, null);
}
}
}
} finally {
lock.unlock();
}
}
public void setResult(int result, MySQLDetector detector, String msg) {
this.isChecking.set(false);
switch (result) {
case OK_STATUS:
setOk(detector);
break;
case ERROR_STATUS:
setError(detector);
break;
case TIMEOUT_STATUS:
setTimeout(detector);
break;
}
if (this.status != OK_STATUS) {
switchSourceIfNeed("heartbeat error");
}
}
private void setOk(MySQLDetector detector) {
switch (status) {
case DBHeartbeat.TIMEOUT_STATUS:
this.status = DBHeartbeat.INIT_STATUS;
this.errorCount = 0;
if (isStop.get()) {
detector.quit();
} else {
heartbeat();// timeout, heart beat again
}
break;
case DBHeartbeat.OK_STATUS:
break;
default:
this.status = OK_STATUS;
this.errorCount = 0;
}
if (isStop.get()) {
detector.quit();
}
}
private void setError(MySQLDetector detector) {
// should continues check error status
if (++errorCount < maxRetryCount) {
if (detector != null && !detector.isQuit()) {
heartbeat(); // error count not enough, heart beat again
}
}else
{
if (detector != null ) {
detector.quit();
}
this.status = ERROR_STATUS;
this.errorCount = 0;
}
}
private void setTimeout(MySQLDetector detector) {
this.isChecking.set(false);
status = DBHeartbeat.TIMEOUT_STATUS;
}
/**
* switch data source
*/
private void switchSourceIfNeed(String reason) {
int switchType = source.getHostConfig().getSwitchType();
if (switchType == DataHostConfig.NOT_SWITCH_DS) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("not switch datasource ,for switchType is "
+ DataHostConfig.NOT_SWITCH_DS);
return;
}
return;
}
PhysicalDBPool pool = this.source.getDbPool();
int curDatasourceHB = pool.getSource().getHeartbeat().getStatus();
// read node can't switch ,only write node can switch
if (pool.getWriteType() == PhysicalDBPool.WRITE_ONLYONE_NODE
&& !source.isReadNode()
&& curDatasourceHB != DBHeartbeat.OK_STATUS
&& pool.getSources().length > 1) {
synchronized (pool) {
// try to see if need switch datasource
curDatasourceHB = pool.getSource().getHeartbeat().getStatus();
if (curDatasourceHB != DBHeartbeat.INIT_STATUS && curDatasourceHB != DBHeartbeat.OK_STATUS) {
int curIndex = pool.getActivedIndex();
int nextId = pool.next(curIndex);
PhysicalDatasource[] allWriteNodes = pool.getSources();
while (true) {
if (nextId == curIndex) {
break;
}
PhysicalDatasource theSource = allWriteNodes[nextId];
DBHeartbeat theSourceHB = theSource.getHeartbeat();
int theSourceHBStatus = theSourceHB.getStatus();
if (theSourceHBStatus == DBHeartbeat.OK_STATUS) {
if (switchType == DataHostConfig.SYN_STATUS_SWITCH_DS) {
if (Integer.valueOf(0).equals( theSourceHB.getSlaveBehindMaster())) {
LOGGER.info("try to switch datasource ,slave is synchronized to master " + theSource.getConfig());
pool.switchSource(nextId, true, reason);
break;
} else {
LOGGER.warn("ignored datasource ,slave is not synchronized to master , slave behind master :"
+ theSourceHB.getSlaveBehindMaster()
+ " " + theSource.getConfig());
}
} else {
// normal switch
LOGGER.info("try to switch datasource ,not checked slave synchronize status " + theSource.getConfig());
pool.switchSource(nextId, true, reason);
break;
}
}
nextId = pool.next(nextId);
}
}
}
}
}
}
@@ -0,0 +1,864 @@
package io.mycat.backend.jdbc;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.sql.*;
import java.util.*;
import io.mycat.backend.mysql.PacketUtil;
import io.mycat.route.Procedure;
import io.mycat.route.ProcedureParameter;
import io.mycat.util.*;
import org.slf4j.Logger; import org.slf4j.LoggerFactory;
import io.mycat.MycatServer;
import io.mycat.backend.BackendConnection;
import io.mycat.backend.mysql.nio.handler.ConnectionHeartBeatHandler;
import io.mycat.backend.mysql.nio.handler.ResponseHandler;
import io.mycat.config.ErrorCode;
import io.mycat.config.Isolations;
import io.mycat.net.NIOProcessor;
import io.mycat.net.mysql.EOFPacket;
import io.mycat.net.mysql.ErrorPacket;
import io.mycat.net.mysql.FieldPacket;
import io.mycat.net.mysql.OkPacket;
import io.mycat.net.mysql.ResultSetHeaderPacket;
import io.mycat.net.mysql.RowDataPacket;
import io.mycat.route.RouteResultsetNode;
import io.mycat.server.ServerConnection;
import io.mycat.server.parser.ServerParse;
public class JDBCConnection implements BackendConnection {
protected static final Logger LOGGER = LoggerFactory
.getLogger(JDBCConnection.class);
private JDBCDatasource pool;
private volatile String schema;
private volatile String dbType;
private volatile String oldSchema;
private byte packetId;
private int txIsolation;
private volatile boolean running = false;
private volatile boolean borrowed;
private long id = 0;
private String host;
private int port;
private Connection con;
private ResponseHandler respHandler;
private volatile Object attachement;
boolean headerOutputed = false;
private volatile boolean modifiedSQLExecuted;
private final long startTime;
private long lastTime;
private boolean isSpark = false;
private NIOProcessor processor;
public NIOProcessor getProcessor() {
return processor;
}
public void setProcessor(NIOProcessor processor) {
this.processor = processor;
}
public JDBCConnection() {
startTime = System.currentTimeMillis();
}
public Connection getCon() {
return con;
}
public void setCon(Connection con) {
this.con = con;
}
@Override
public void close(String reason) {
try {
con.close();
if(processor!=null){
processor.removeConnection(this);
}
} catch (SQLException e) {
}
}
public void setId(long id) {
this.id = id;
}
public JDBCDatasource getPool() {
return pool;
}
public void setPool(JDBCDatasource pool) {
this.pool = pool;
}
public void setHost(String host) {
this.host = host;
}
public void setPort(int port) {
this.port = port;
}
@Override
public boolean isClosed() {
try {
return con == null || con.isClosed();
} catch (SQLException e) {
return true;
}
}
@Override
public void idleCheck() {
if(TimeUtil.currentTimeMillis() > lastTime + pool.getConfig().getIdleTimeout()){
close(" idle check");
}
}
@Override
public long getStartupTime() {
return startTime;
}
@Override
public String getHost() {
return this.host;
}
@Override
public int getPort() {
return this.port;
}
@Override
public int getLocalPort() {
return 0;
}
@Override
public long getNetInBytes() {
return 0;
}
@Override
public long getNetOutBytes() {
return 0;
}
@Override
public boolean isModifiedSQLExecuted() {
return modifiedSQLExecuted;
}
@Override
public boolean isFromSlaveDB() {
return false;
}
public String getDbType() {
return this.dbType;
}
public void setDbType(String newDbType) {
this.dbType = newDbType.toUpperCase();
this.isSpark = dbType.equals("SPARK");
}
@Override
public String getSchema() {
return this.schema;
}
@Override
public void setSchema(String newSchema) {
this.oldSchema = this.schema;
this.schema = newSchema;
}
@Override
public long getLastTime() {
return lastTime;
}
@Override
public boolean isClosedOrQuit() {
return this.isClosed();
}
@Override
public void setAttachment(Object attachment) {
this.attachement = attachment;
}
@Override
public void quit() {
this.close("client quit");
}
@Override
public void setLastTime(long currentTimeMillis) {
this.lastTime = currentTimeMillis;
}
@Override
public void release() {
modifiedSQLExecuted = false;
setResponseHandler(null);
pool.releaseChannel(this);
}
public void setRunning(boolean running) {
this.running = running;
}
@Override
public boolean setResponseHandler(ResponseHandler commandHandler) {
respHandler = commandHandler;
return false;
}
@Override
public void commit() {
try {
con.commit();
this.respHandler.okResponse(OkPacket.OK, this);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
private int convertNativeIsolationToJDBC(int nativeIsolation)
{
if(nativeIsolation== Isolations.REPEATED_READ)
{
return Connection.TRANSACTION_REPEATABLE_READ;
}else
if(nativeIsolation== Isolations.SERIALIZABLE)
{
return Connection.TRANSACTION_SERIALIZABLE;
} else
{
return nativeIsolation;
}
}
private void syncIsolation(int nativeIsolation)
{
int jdbcIsolation=convertNativeIsolationToJDBC(nativeIsolation);
int srcJdbcIsolation= getTxIsolation();
if (jdbcIsolation == srcJdbcIsolation || "oracle".equalsIgnoreCase(getDbType())
&& jdbcIsolation != Connection.TRANSACTION_READ_COMMITTED
&& jdbcIsolation != Connection.TRANSACTION_SERIALIZABLE) {
return;
}
try
{
con.setTransactionIsolation(jdbcIsolation);
} catch (SQLException e)
{
LOGGER.warn("set txisolation error:",e);
}
}
private void executeSQL(RouteResultsetNode rrn, ServerConnection sc,
boolean autocommit) throws IOException {
String orgin = rrn.getStatement();
// String sql = rrn.getStatement().toLowerCase();
// LOGGER.info("JDBC SQL:"+orgin+"|"+sc.toString());
if (!modifiedSQLExecuted && rrn.isModifySQL()) {
modifiedSQLExecuted = true;
}
try {
syncIsolation(sc.getTxIsolation()) ;
if (!this.schema.equals(this.oldSchema)) {
con.setCatalog(schema);
this.oldSchema = schema;
}
if (!this.isSpark) {
con.setAutoCommit(autocommit);
}
int sqlType = rrn.getSqlType();
if(rrn.isCallStatement()&&"oracle".equalsIgnoreCase(getDbType()))
{
//存储过程暂时只支持oracle
ouputCallStatement(rrn,sc,orgin);
} else
if (sqlType == ServerParse.SELECT || sqlType == ServerParse.SHOW) {
if ((sqlType == ServerParse.SHOW) && (!dbType.equals("MYSQL"))) {
// showCMD(sc, orgin);
//ShowVariables.execute(sc, orgin);
ShowVariables.execute(sc, orgin,this);
} else if ("SELECT CONNECTION_ID()".equalsIgnoreCase(orgin)) {
//ShowVariables.justReturnValue(sc,String.valueOf(sc.getId()));
ShowVariables.justReturnValue(sc,String.valueOf(sc.getId()),this);
} else {
ouputResultSet(sc, orgin);
}
} else {
executeddl(sc, orgin);
}
} catch (SQLException e) {
String msg = e.getMessage();
ErrorPacket error = new ErrorPacket();
error.packetId = ++packetId;
error.errno = e.getErrorCode();
error.message = msg.getBytes();
this.respHandler.errorResponse(error.writeToBytes(sc), this);
}
catch (Exception e) {
String msg = e.getMessage();
ErrorPacket error = new ErrorPacket();
error.packetId = ++packetId;
error.errno = ErrorCode.ER_UNKNOWN_ERROR;
error.message = ((msg == null) ? e.toString().getBytes() : msg.getBytes());
String err = null;
if(error.message!=null){
err = new String(error.message);
}
LOGGER.error("sql execute error, "+ err , e);
this.respHandler.errorResponse(error.writeToBytes(sc), this);
}
finally {
this.running = false;
}
}
private FieldPacket getNewFieldPacket(String charset, String fieldName) {
FieldPacket fieldPacket = new FieldPacket();
fieldPacket.orgName = StringUtil.encode(fieldName, charset);
fieldPacket.name = StringUtil.encode(fieldName, charset);
fieldPacket.length = 20;
fieldPacket.flags = 0;
fieldPacket.decimals = 0;
int javaType = 12;
fieldPacket.type = (byte) (MysqlDefs.javaTypeMysql(javaType) & 0xff);
return fieldPacket;
}
private void executeddl(ServerConnection sc, String sql)
throws SQLException {
Statement stmt = null;
try {
stmt = con.createStatement();
int count = stmt.executeUpdate(sql);
OkPacket okPck = new OkPacket();
okPck.affectedRows = count;
okPck.insertId = 0;
okPck.packetId = ++packetId;
okPck.message = " OK!".getBytes();
this.respHandler.okResponse(okPck.writeToBytes(sc), this);
} finally {
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
}
}
}
}
private static int oracleCURSORTypeValue=-10;
static
{
Object cursor = ObjectUtil.getStaticFieldValue("oracle.jdbc.OracleTypes", "CURSOR");
if(cursor!=null) {
oracleCURSORTypeValue = (int) cursor;
}
}
private void ouputCallStatement(RouteResultsetNode rrn,ServerConnection sc, String sql)
throws SQLException {
CallableStatement stmt = null;
ResultSet rs = null;
try {
Procedure procedure = rrn.getProcedure();
Collection<ProcedureParameter> paramters= procedure.getParamterMap().values();
String callSql = procedure.toPreCallSql(null);
stmt = con.prepareCall(callSql);
for (ProcedureParameter paramter : paramters)
{
if((ProcedureParameter.IN.equalsIgnoreCase(paramter.getParameterType())
||ProcedureParameter.INOUT.equalsIgnoreCase(paramter.getParameterType())))
{
Object value= paramter.getValue()!=null ?paramter.getValue():paramter.getName();
stmt.setObject(paramter.getIndex(),value);
}
if(ProcedureParameter.OUT.equalsIgnoreCase(paramter.getParameterType())
||ProcedureParameter.INOUT.equalsIgnoreCase(paramter.getParameterType()) )
{
int jdbcType ="oracle".equalsIgnoreCase(getDbType())&& procedure.getListFields().contains(paramter.getName())?oracleCURSORTypeValue: paramter.getJdbcType();
stmt.registerOutParameter(paramter.getIndex(), jdbcType);
}
}
boolean hadResults= stmt.execute();
ByteBuffer byteBuf = sc.allocate();
if(procedure.getSelectColumns().size()>0)
{
List<FieldPacket> fieldPks = new LinkedList<FieldPacket>();
for (ProcedureParameter paramter : paramters)
{
if (!procedure.getListFields().contains(paramter.getName())&&(ProcedureParameter.OUT.equalsIgnoreCase(paramter.getParameterType())
|| ProcedureParameter.INOUT.equalsIgnoreCase(paramter.getParameterType())) )
{
FieldPacket packet = PacketUtil.getField(paramter.getName(), MysqlDefs.javaTypeMysql(paramter.getJdbcType()));
fieldPks.add(packet);
}
}
int colunmCount = fieldPks.size();
ResultSetHeaderPacket headerPkg = new ResultSetHeaderPacket();
headerPkg.fieldCount = fieldPks.size();
headerPkg.packetId = ++packetId;
byteBuf = headerPkg.write(byteBuf, sc, true);
byteBuf.flip();
byte[] header = new byte[byteBuf.limit()];
byteBuf.get(header);
byteBuf.clear();
List<byte[]> fields = new ArrayList<byte[]>(fieldPks.size());
Iterator<FieldPacket> itor = fieldPks.iterator();
while (itor.hasNext()) {
FieldPacket curField = itor.next();
curField.packetId = ++packetId;
byteBuf = curField.write(byteBuf, sc, false);
byteBuf.flip();
byte[] field = new byte[byteBuf.limit()];
byteBuf.get(field);
byteBuf.clear();
fields.add(field);
itor.remove();
}
EOFPacket eofPckg = new EOFPacket();
eofPckg.packetId = ++packetId;
byteBuf = eofPckg.write(byteBuf, sc, false);
byteBuf.flip();
byte[] eof = new byte[byteBuf.limit()];
byteBuf.get(eof);
byteBuf.clear();
this.respHandler.fieldEofResponse(header, fields, eof, this);
RowDataPacket curRow = new RowDataPacket(colunmCount);
for (String name : procedure.getSelectColumns())
{
ProcedureParameter procedureParameter= procedure.getParamterMap().get(name);
curRow.add(StringUtil.encode(String.valueOf(stmt.getObject(procedureParameter.getIndex())),
sc.getCharset()));
}
curRow.packetId = ++packetId;
byteBuf = curRow.write(byteBuf, sc, false);
byteBuf.flip();
byte[] row = new byte[byteBuf.limit()];
byteBuf.get(row);
byteBuf.clear();
this.respHandler.rowResponse(row, this);
eofPckg = new EOFPacket();
eofPckg.packetId = ++packetId;
if(procedure.isResultList())
{
eofPckg.status = 42;
}
byteBuf = eofPckg.write(byteBuf, sc, false);
byteBuf.flip();
eof = new byte[byteBuf.limit()];
byteBuf.get(eof);
byteBuf.clear();
this.respHandler.rowEofResponse(eof, this);
}
if(procedure.isResultList())
{
List<FieldPacket> fieldPks = new LinkedList<FieldPacket>();
int listSize=procedure.getListFields().size();
for (ProcedureParameter paramter : paramters)
{
if (procedure.getListFields().contains(paramter.getName())&&(ProcedureParameter.OUT.equalsIgnoreCase(paramter.getParameterType())
|| ProcedureParameter.INOUT.equalsIgnoreCase(paramter.getParameterType())) )
{
listSize--;
Object object = stmt.getObject(paramter.getIndex());
rs= (ResultSet) object;
if(rs==null) {
continue;
}
ResultSetUtil.resultSetToFieldPacket(sc.getCharset(), fieldPks, rs,
this.isSpark);
int colunmCount = fieldPks.size();
ResultSetHeaderPacket headerPkg = new ResultSetHeaderPacket();
headerPkg.fieldCount = fieldPks.size();
headerPkg.packetId = ++packetId;
byteBuf = headerPkg.write(byteBuf, sc, true);
byteBuf.flip();
byte[] header = new byte[byteBuf.limit()];
byteBuf.get(header);
byteBuf.clear();
List<byte[]> fields = new ArrayList<byte[]>(fieldPks.size());
Iterator<FieldPacket> itor = fieldPks.iterator();
while (itor.hasNext()) {
FieldPacket curField = itor.next();
curField.packetId = ++packetId;
byteBuf = curField.write(byteBuf, sc, false);
byteBuf.flip();
byte[] field = new byte[byteBuf.limit()];
byteBuf.get(field);
byteBuf.clear();
fields.add(field);
itor.remove();
}
EOFPacket eofPckg = new EOFPacket();
eofPckg.packetId = ++packetId;
byteBuf = eofPckg.write(byteBuf, sc, false);
byteBuf.flip();
byte[] eof = new byte[byteBuf.limit()];
byteBuf.get(eof);
byteBuf.clear();
this.respHandler.fieldEofResponse(header, fields, eof, this);
// output row
while (rs.next()) {
RowDataPacket curRow = new RowDataPacket(colunmCount);
for (int i = 0; i < colunmCount; i++) {
int j = i + 1;
curRow.add(StringUtil.encode(rs.getString(j),
sc.getCharset()));
}
curRow.packetId = ++packetId;
byteBuf = curRow.write(byteBuf, sc, false);
byteBuf.flip();
byte[] row = new byte[byteBuf.limit()];
byteBuf.get(row);
byteBuf.clear();
this.respHandler.rowResponse(row, this);
}
eofPckg = new EOFPacket();
eofPckg.packetId = ++packetId;
if(listSize!=0)
{
eofPckg.status = 42;
}
byteBuf = eofPckg.write(byteBuf, sc, false);
byteBuf.flip();
eof = new byte[byteBuf.limit()];
byteBuf.get(eof);
byteBuf.clear();
this.respHandler.rowEofResponse(eof, this);
}
}
}
if(!procedure.isResultSimpleValue())
{
byte[] OK = new byte[] { 7, 0, 0, 1, 0, 0, 0, 2, 0, 0,
0 };
OK[3]=++packetId;
this.respHandler.okResponse(OK,this);
}
sc.recycle(byteBuf);
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
}
}
}
}
private void ouputResultSet(ServerConnection sc, String sql)
throws SQLException {
ResultSet rs = null;
Statement stmt = null;
try {
stmt = con.createStatement();
rs = stmt.executeQuery(sql);
List<FieldPacket> fieldPks = new LinkedList<FieldPacket>();
ResultSetUtil.resultSetToFieldPacket(sc.getCharset(), fieldPks, rs,
this.isSpark);
int colunmCount = fieldPks.size();
ByteBuffer byteBuf = sc.allocate();
ResultSetHeaderPacket headerPkg = new ResultSetHeaderPacket();
headerPkg.fieldCount = fieldPks.size();
headerPkg.packetId = ++packetId;
byteBuf = headerPkg.write(byteBuf, sc, true);
byteBuf.flip();
byte[] header = new byte[byteBuf.limit()];
byteBuf.get(header);
byteBuf.clear();
List<byte[]> fields = new ArrayList<byte[]>(fieldPks.size());
Iterator<FieldPacket> itor = fieldPks.iterator();
while (itor.hasNext()) {
FieldPacket curField = itor.next();
curField.packetId = ++packetId;
byteBuf = curField.write(byteBuf, sc, false);
byteBuf.flip();
byte[] field = new byte[byteBuf.limit()];
byteBuf.get(field);
byteBuf.clear();
fields.add(field);
itor.remove();
}
EOFPacket eofPckg = new EOFPacket();
eofPckg.packetId = ++packetId;
byteBuf = eofPckg.write(byteBuf, sc, false);
byteBuf.flip();
byte[] eof = new byte[byteBuf.limit()];
byteBuf.get(eof);
byteBuf.clear();
this.respHandler.fieldEofResponse(header, fields, eof, this);
// output row
while (rs.next()) {
RowDataPacket curRow = new RowDataPacket(colunmCount);
for (int i = 0; i < colunmCount; i++) {
int j = i + 1;
curRow.add(StringUtil.encode(rs.getString(j),
sc.getCharset()));
}
curRow.packetId = ++packetId;
byteBuf = curRow.write(byteBuf, sc, false);
byteBuf.flip();
byte[] row = new byte[byteBuf.limit()];
byteBuf.get(row);
byteBuf.clear();
this.respHandler.rowResponse(row, this);
}
// end row
eofPckg = new EOFPacket();
eofPckg.packetId = ++packetId;
byteBuf = eofPckg.write(byteBuf, sc, false);
byteBuf.flip();
eof = new byte[byteBuf.limit()];
byteBuf.get(eof);
sc.recycle(byteBuf);
this.respHandler.rowEofResponse(eof, this);
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
}
}
}
}
@Override
public void query(final String sql) throws UnsupportedEncodingException {
if(respHandler instanceof ConnectionHeartBeatHandler)
{
justForHeartbeat(sql);
} else
{
throw new UnsupportedEncodingException("unsupported yet ");
}
}
private void justForHeartbeat(String sql)
{
Statement stmt = null;
try {
stmt = con.createStatement();
stmt.execute(sql);
if(!isAutocommit()){ //如果在写库上,如果是事务方式的连接,需要进行手动commit
con.commit();
}
this.respHandler.okResponse(OkPacket.OK, this);
}
catch (Exception e)
{
String msg = e.getMessage();
ErrorPacket error = new ErrorPacket();
error.packetId = ++packetId;
error.errno = ErrorCode.ER_UNKNOWN_ERROR;
error.message = msg.getBytes();
this.respHandler.errorResponse(error.writeToBytes(), this);
}
finally {
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
}
}
}
}
@Override
public Object getAttachment() {
return this.attachement;
}
@Override
public String getCharset() {
return null;
}
@Override
public void execute(final RouteResultsetNode node,
final ServerConnection source, final boolean autocommit)
throws IOException {
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
executeSQL(node, source, autocommit);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
};
MycatServer.getInstance().getBusinessExecutor().execute(runnable);
}
@Override
public void recordSql(String host, String schema, String statement) {
}
@Override
public boolean syncAndExcute() {
return true;
}
@Override
public void rollback() {
try {
con.rollback();
this.respHandler.okResponse(OkPacket.OK, this);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public boolean isRunning() {
return this.running;
}
@Override
public boolean isBorrowed() {
return this.borrowed;
}
@Override
public void setBorrowed(boolean borrowed) {
this.borrowed = borrowed;
}
@Override
public int getTxIsolation() {
if (con != null) {
try {
return con.getTransactionIsolation();
} catch (SQLException e) {
return 0;
}
} else {
return -1;
}
}
@Override
public boolean isAutocommit() {
if (con == null) {
return true;
} else {
try {
return con.getAutoCommit();
} catch (SQLException e) {
}
}
return true;
}
@Override
public long getId() {
return id;
}
@Override
public String toString() {
return "JDBCConnection [id=" + id +",autocommit="+this.isAutocommit()+",pool=" + pool + ", schema=" + schema + ", dbType=" + dbType + ", oldSchema="
+ oldSchema + ", packetId=" + packetId + ", txIsolation=" + txIsolation + ", running=" + running
+ ", borrowed=" + borrowed + ", host=" + host + ", port=" + port + ", con=" + con
+ ", respHandler=" + respHandler + ", attachement=" + attachement + ", headerOutputed="
+ headerOutputed + ", modifiedSQLExecuted=" + modifiedSQLExecuted + ", startTime=" + startTime
+ ", lastTime=" + lastTime + ", isSpark=" + isSpark + ", processor=" + processor + "]";
}
@Override
public void discardClose(String reason) {
// TODO Auto-generated method stub
}
}
@@ -0,0 +1,124 @@
package io.mycat.backend.jdbc;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import com.google.common.collect.Lists;
import io.mycat.MycatServer;
import io.mycat.backend.datasource.PhysicalDatasource;
import io.mycat.backend.heartbeat.DBHeartbeat;
import io.mycat.backend.mysql.nio.handler.ResponseHandler;
import io.mycat.config.model.DBHostConfig;
import io.mycat.config.model.DataHostConfig;
import io.mycat.net.NIOConnector;
import io.mycat.net.NIOProcessor;
public class JDBCDatasource extends PhysicalDatasource {
static {
// 加载可能的驱动
List<String> drivers = Lists.newArrayList(
"com.mysql.jdbc.Driver",
"io.mycat.backend.jdbc.mongodb.MongoDriver",
"io.mycat.backend.jdbc.sequoiadb.SequoiaDriver",
"oracle.jdbc.OracleDriver",
"com.microsoft.sqlserver.jdbc.SQLServerDriver",
"org.apache.hive.jdbc.HiveDriver",
"com.ibm.db2.jcc.DB2Driver",
"org.postgresql.Driver");
for (String driver : drivers) {
try {
Class.forName(driver);
} catch (ClassNotFoundException ignored) {
}
}
}
public JDBCDatasource(DBHostConfig config, DataHostConfig hostConfig, boolean isReadNode) {
super(config, hostConfig, isReadNode);
}
@Override
public DBHeartbeat createHeartBeat() {
return new JDBCHeartbeat(this);
}
@Override
public void createNewConnection(ResponseHandler handler,String schema) throws IOException {
DBHostConfig cfg = getConfig();
JDBCConnection c = new JDBCConnection();
c.setHost(cfg.getIp());
c.setPort(cfg.getPort());
c.setPool(this);
c.setSchema(schema);
c.setDbType(cfg.getDbType());
NIOProcessor processor = (NIOProcessor) MycatServer.getInstance().nextProcessor();
c.setProcessor(processor);
c.setId(NIOConnector.ID_GENERATOR.getId()); //复用mysql的Backend的ID,需要在process中存储
processor.addBackend(c);
try {
Connection con = getConnection();
// c.setIdleTimeout(pool.getConfig().getIdleTimeout());
c.setCon(con);
// notify handler
handler.connectionAcquired(c);
} catch (Exception e) {
handler.connectionError(e, c);
}
}
@Override
public boolean testConnection(String schema) throws IOException {
boolean isConnected = false;
Connection connection = null;
Statement statement = null;
try {
DBHostConfig cfg = getConfig();
connection = DriverManager.getConnection(cfg.getUrl(), cfg.getUser(), cfg.getPassword());
statement = connection.createStatement();
if (connection != null && statement != null) {
isConnected = true;
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (statement != null) {
try { statement.close(); } catch (SQLException e) {}
}
if (connection != null) {
try { connection.close(); } catch (SQLException e) {}
}
}
return isConnected;
}
Connection getConnection() throws SQLException {
DBHostConfig cfg = getConfig();
Connection connection = DriverManager.getConnection(cfg.getUrl(), cfg.getUser(), cfg.getPassword());
String initSql=getHostConfig().getConnectionInitSql();
if (initSql != null && !"".equals(initSql)) {
Statement statement = null;
try {
statement = connection.createStatement();
statement.execute(initSql);
} finally {
if (statement != null) {
statement.close();
}
}
}
return connection;
}
}
@@ -0,0 +1,123 @@
package io.mycat.backend.jdbc;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.locks.ReentrantLock;
import org.slf4j.Logger; import org.slf4j.LoggerFactory;
import io.mycat.backend.heartbeat.DBHeartbeat;
import io.mycat.statistic.HeartbeatRecorder;
public class JDBCHeartbeat extends DBHeartbeat{
private final ReentrantLock lock;
private final JDBCDatasource source;
private final boolean heartbeatnull;
private Long lastSendTime = System.currentTimeMillis();
private Long lastReciveTime = System.currentTimeMillis();
private Logger logger = LoggerFactory.getLogger(this.getClass());
public JDBCHeartbeat(JDBCDatasource source)
{
this.source = source;
lock = new ReentrantLock(false);
this.status = INIT_STATUS;
this.heartbeatSQL = source.getHostConfig().getHearbeatSQL().trim();
this.heartbeatnull= heartbeatSQL.length()==0;
}
@Override
public void start()
{
if (this.heartbeatnull){
stop();
return;
}
lock.lock();
try
{
isStop.compareAndSet(true, false);
this.status = DBHeartbeat.OK_STATUS;
} finally
{
lock.unlock();
}
}
@Override
public void stop()
{
lock.lock();
try
{
if (isStop.compareAndSet(false, true))
{
isChecking.set(false);
}
} finally
{
lock.unlock();
}
}
@Override
public String getLastActiveTime()
{
long t = lastReciveTime;
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(new Date(t));
}
@Override
public long getTimeout()
{
return 0;
}
@Override
public HeartbeatRecorder getRecorder() {
recorder.set(lastReciveTime - lastSendTime);
return recorder;
}
@Override
public void heartbeat()
{
if (isStop.get()) {
return;
}
lastSendTime = System.currentTimeMillis();
lock.lock();
try
{
isChecking.set(true);
try (Connection c = source.getConnection())
{
try (Statement s = c.createStatement())
{
s.execute(heartbeatSQL);
}
}
status = OK_STATUS;
if(logger.isDebugEnabled()){
logger.debug("JDBCHeartBeat con query sql: "+heartbeatSQL);
}
} catch (Exception ex)
{
logger.error("JDBCHeartBeat error",ex);
status = ERROR_STATUS;
} finally
{
lock.unlock();
this.isChecking.set(false);
lastReciveTime = System.currentTimeMillis();
}
}
}
@@ -0,0 +1,198 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat.backend.jdbc;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.slf4j.Logger; import org.slf4j.LoggerFactory;
import io.mycat.backend.BackendConnection;
import io.mycat.backend.mysql.PacketUtil;
import io.mycat.config.Fields;
import io.mycat.net.mysql.EOFPacket;
import io.mycat.net.mysql.FieldPacket;
import io.mycat.net.mysql.ResultSetHeaderPacket;
import io.mycat.net.mysql.RowDataPacket;
import io.mycat.server.NonBlockingSession;
import io.mycat.server.ServerConnection;
import io.mycat.util.StringUtil;
/**
* @author mycat
*/
public final class ShowVariables
{
private static final Logger LOGGER = LoggerFactory.getLogger(ShowVariables.class);
private static final int FIELD_COUNT = 2;
private static final ResultSetHeaderPacket header = PacketUtil.getHeader(FIELD_COUNT);
private static final FieldPacket[] fields = new FieldPacket[FIELD_COUNT];
private static final EOFPacket eof = new EOFPacket();
private static final Pattern pattern = Pattern.compile("(?:like|=)\\s*'([^']*(?:\\w+)+[^']*)+'",Pattern.CASE_INSENSITIVE);
static {
int i = 0;
byte packetId = 0;
header.packetId = ++packetId;
fields[i] = PacketUtil.getField("VARIABLE_NAME", Fields.FIELD_TYPE_VAR_STRING);
fields[i++].packetId = ++packetId;
fields[i] = PacketUtil.getField("VALUE", Fields.FIELD_TYPE_VAR_STRING);
fields[i++].packetId = ++packetId;
eof.packetId = ++packetId;
}
private static List<String> parseVariable(String sql)
{
List<String> variableList=new ArrayList<>();
Matcher matcher = pattern.matcher(sql);
while (matcher.find())
{
variableList.add(matcher.group(1));
}
return variableList;
}
public static void execute(ServerConnection c, String sql) {
ByteBuffer buffer = c.allocate();
// write header
buffer = header.write(buffer, c,true);
// write fields
for (FieldPacket field : fields) {
buffer = field.write(buffer, c,true);
}
// write eof
buffer = eof.write(buffer, c,true);
// write rows
byte packetId = eof.packetId;
List<String> variableList= parseVariable(sql);
for (String key : variableList)
{
String value= variables.get(key) ;
if(value!=null)
{
RowDataPacket row = getRow(key, value, c.getCharset());
row.packetId = ++packetId;
buffer = row.write(buffer, c,true);
}
}
// write lastEof
EOFPacket lastEof = new EOFPacket();
lastEof.packetId = ++packetId;
buffer = lastEof.write(buffer, c,true);
// write buffer
c.write(buffer);
}
public static void justReturnValue(ServerConnection c, String value) {
ByteBuffer buffer = c.allocate();
// write header
buffer = header.write(buffer, c,true);
// write fields
for (FieldPacket field : fields) {
buffer = field.write(buffer, c,true);
}
// write eof
buffer = eof.write(buffer, c,true);
// write rows
byte packetId = eof.packetId;
if(value!=null)
{
RowDataPacket row = new RowDataPacket(1);
row.add(StringUtil.encode(value, c.getCharset()));
row.packetId = ++packetId;
buffer = row.write(buffer, c,true);
}
// write lastEof
EOFPacket lastEof = new EOFPacket();
lastEof.packetId = ++packetId;
buffer = lastEof.write(buffer, c,true);
// write buffer
c.write(buffer);
}
private static RowDataPacket getRow(String name, String value, String charset) {
RowDataPacket row = new RowDataPacket(FIELD_COUNT);
row.add(StringUtil.encode(name, charset));
row.add(StringUtil.encode(value, charset));
return row;
}
private static final Map<String, String> variables = new HashMap<String, String>();
static {
variables.put("character_set_client", "utf8");
variables.put("character_set_connection", "utf8");
variables.put("character_set_results", "utf8");
variables.put("character_set_server", "utf8");
variables.put("init_connect", "");
variables.put("interactive_timeout", "172800");
variables.put("lower_case_table_names", "1");
variables.put("max_allowed_packet", "16777216");
variables.put("net_buffer_length", "16384");
variables.put("net_write_timeout", "60");
variables.put("query_cache_size", "0");
variables.put("query_cache_type", "OFF");
variables.put("sql_mode", "STRICT_TRANS_TABLES");
variables.put("system_time_zone", "CST");
variables.put("time_zone", "SYSTEM");
variables.put("tx_isolation", "REPEATABLE-READ");
variables.put("wait_timeout", "172800");
}
public static void execute(ServerConnection sc, String orgin, BackendConnection jdbcConnection) {
execute(sc, orgin);
NonBlockingSession session = sc.getSession2();
session.releaseConnectionIfSafe(jdbcConnection, LOGGER.isDebugEnabled(), false);
}
public static void justReturnValue(ServerConnection sc, String orgin, BackendConnection jdbcConnection) {
justReturnValue(sc, orgin);
NonBlockingSession session = sc.getSession2();
session.releaseConnectionIfSafe(jdbcConnection, LOGGER.isDebugEnabled(), false);
}
}
@@ -0,0 +1,70 @@
package io.mycat.backend.jdbc.mongodb;
import java.sql.DriverPropertyInfo;
import java.util.ArrayList;
public class DriverPropertyInfoHelper{
public static final String AUTO_CONNECT_RETRY = "autoConnectRetry";
public static final String CONNECTIONS_PER_HOST = "connecionsPerHost";
public static final String CONNECT_TIMEOUT = "connectTimeout";
public static final String CURSOR_FINALIZER_ENABLED = "cursorFinalizerEnabled";
public static final String MAX_AUTO_CONNECT_RETRY_TIME = "maxAutoConnectRetryTime";
public static final String READ_PREFERENCE = "readPreference";
public static final String SOCKET_TIMEOUT = "socketTimeout";
public DriverPropertyInfo[] getPropertyInfo()
{
ArrayList<DriverPropertyInfo> propInfos = new ArrayList<DriverPropertyInfo>();
addPropInfo(
propInfos,
AUTO_CONNECT_RETRY,
"false",
"If true, the driver will keep trying to connect to the same server in case that the socket "
+ "cannot be established. There is maximum amount of time to keep retrying, which is 15s by "
+ "default.", null);
addPropInfo(propInfos, CONNECTIONS_PER_HOST, "10", "The maximum number of connections allowed per "
+ "host for this Mongo instance. Those connections will be kept in a pool when idle.", null);
addPropInfo(propInfos, CONNECT_TIMEOUT, "10000", "The connection timeout in milliseconds. ", null);
addPropInfo(propInfos, CURSOR_FINALIZER_ENABLED, "true", "Sets whether there is a a finalize "
+ "method created that cleans up instances of DBCursor that the client does not close.",
null);
addPropInfo(propInfos, MAX_AUTO_CONNECT_RETRY_TIME, "0",
"The maximum amount of time in MS to spend retrying to open connection to the same server."
+ "Default is 0, which means to use the default 15s if autoConnectRetry is on.", null);
addPropInfo(propInfos, READ_PREFERENCE, "primary",
"represents preferred replica set members to which a query or command can be sent", new String[] {
"primary", "primary preferred", "secondary", "secondary preferred", "nearest" });
addPropInfo(propInfos, SOCKET_TIMEOUT, "0", "The socket timeout in milliseconds It is used for "
+ "I/O socket read and write operations "
+ "Socket.setSoTimeout(int) Default is 0 and means no timeout.", null);
return propInfos.toArray(new DriverPropertyInfo[propInfos.size()]);
}
private void addPropInfo(final ArrayList<DriverPropertyInfo> propInfos, final String propName,
final String defaultVal, final String description, final String[] choices)
{
DriverPropertyInfo newProp = new DriverPropertyInfo(propName, defaultVal);
newProp.description = description;
if (choices != null)
{
newProp.choices = choices;
}
propInfos.add(newProp);
}
}
@@ -0,0 +1,401 @@
package io.mycat.backend.jdbc.mongodb;
import java.net.UnknownHostException;
import java.sql.Array;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.NClob;
import java.sql.PreparedStatement;
import java.sql.SQLClientInfoException;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Savepoint;
import java.sql.Statement;
import java.sql.Struct;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;
import com.mongodb.DB;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientURI;
/**
* 功能详细描述
* @author sohudo[http://blog.csdn.net/wind520]
* @create 2014年12月19日 下午6:50:23
* @version 0.0.1
*/
public class MongoConnection implements Connection {
//private String url = null;
private MongoClient mc = null;
private boolean isClosed = false;
private String _schema;
private Properties _clientInfo;
public MongoConnection(MongoClientURI mcu, String url) throws UnknownHostException {
// this.url = url;
this._schema = mcu.getDatabase();
mc = new MongoClient(mcu);
}
public DB getDB() {
if (this._schema!=null) {
return this.mc.getDB(this._schema);
}
else {
return null;
}
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
@Override
public String nativeSQL(String sql) throws SQLException {
return sql;
}
@Override
public void setAutoCommit(boolean autoCommit) throws SQLException {
//if (!autoCommit)
// throw new RuntimeException("autoCommit has to be on");
}
@Override
public boolean getAutoCommit() throws SQLException {
return true;//return false;
}
@Override
public void commit() throws SQLException {
}
@Override
public void rollback() throws SQLException {
//throw new RuntimeException("can't rollback");
}
@Override
public void close() throws SQLException {
this.mc=null;
isClosed=true;
}
@Override
public boolean isClosed() throws SQLException {
return isClosed;//return false;
}
@Override
public DatabaseMetaData getMetaData() throws SQLException {
// 获取一个 DatabaseMetaData 对象,该对象包含关于此 Connection 对象所连接的数据库的元数据。
return null;
}
@Override
public void setReadOnly(boolean readOnly) throws SQLException {
//if (readOnly)
// throw new RuntimeException("no read only mode");
}
@Override
public boolean isReadOnly() throws SQLException {
// 查询此 Connection 对象是否处于只读模式。
return false;
}
@Override
public void setCatalog(String catalog) throws SQLException {
this._schema=catalog;
}
@Override
public String getCatalog() throws SQLException {
// 获取此 Connection 对象的当前目录名称
return this._schema;
}
@Override
public void setTransactionIsolation(int level) throws SQLException {
//throw new RuntimeException("no TransactionIsolation");
}
@Override
public int getTransactionIsolation() throws SQLException {
return 0;
}
@Override
public SQLWarning getWarnings() throws SQLException {
return null;//throw new RuntimeException("should do get last error");
}
@Override
public void clearWarnings() throws SQLException {
}
@Override
public Map<String, Class<?>> getTypeMap() throws SQLException {
return null;
}
@Override
public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
}
@Override
public void setHoldability(int holdability) throws SQLException {
// 将使用此 Connection 对象创建的 ResultSet 对象的默认可保存性 (holdability) 更改为给定可保存性。
}
@Override
public int getHoldability() throws SQLException {
// 获取使用此 Connection 对象创建的 ResultSet 对象的当前可保存性。
return 0;
}
@Override
public Savepoint setSavepoint() throws SQLException {
return null;//throw new RuntimeException("no savepoints");
}
@Override
public Savepoint setSavepoint(String name) throws SQLException {
return null;
}
@Override
public void rollback(Savepoint savepoint) throws SQLException {
// throw new RuntimeException("can't rollback");
}
@Override
public void releaseSavepoint(Savepoint savepoint) throws SQLException {
}
@Override
public Statement createStatement() throws SQLException {
// 创建一个 Statement 对象来将 SQL 语句发送到数据库。
return createStatement(0, 0, 0);
}
@Override
public Statement createStatement(int resultSetType, int resultSetConcurrency)
throws SQLException {
// 创建一个 Statement 对象,该对象将生成具有给定类型和并发性的 ResultSet 对象。
return createStatement(resultSetType, resultSetConcurrency, 0);
}
@Override
public Statement createStatement(int resultSetType,
int resultSetConcurrency, int resultSetHoldability)
throws SQLException {
// 创建一个 Statement 对象,该对象将生成具有给定类型、并发性和可保存性的 ResultSet 对象。
return new MongoStatement(this, resultSetType, resultSetConcurrency, resultSetHoldability);
}
@Override
public CallableStatement prepareCall(String sql) throws SQLException {
return prepareCall(sql, 0, 0, 0);
}
@Override
public CallableStatement prepareCall(String sql, int resultSetType,
int resultSetConcurrency) throws SQLException {
return prepareCall(sql, resultSetType, resultSetConcurrency, 0);
}
@Override
public CallableStatement prepareCall(String sql, int resultSetType,
int resultSetConcurrency, int resultSetHoldability)
throws SQLException {
//return null;
throw new RuntimeException("CallableStatement not supported");
}
@Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
return prepareStatement(sql, 0, 0, 0);
}
@Override
public PreparedStatement prepareStatement(String sql, int resultSetType,
int resultSetConcurrency) throws SQLException {
return prepareStatement(sql, resultSetType, resultSetConcurrency, 0);
}
@Override
public PreparedStatement prepareStatement(String sql, int resultSetType,
int resultSetConcurrency, int resultSetHoldability)
throws SQLException {
return new MongoPreparedStatement(this, resultSetType, resultSetConcurrency, resultSetHoldability,sql);
}
@Override
public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys)
throws SQLException {
return null;
}
@Override
public PreparedStatement prepareStatement(String sql, int[] columnIndexes)
throws SQLException {
return null;
}
@Override
public PreparedStatement prepareStatement(String sql, String[] columnNames)
throws SQLException {
return null;
}
@Override
public Clob createClob() throws SQLException {
return null;
}
@Override
public Blob createBlob() throws SQLException {
return null;
}
@Override
public NClob createNClob() throws SQLException {
return null;
}
@Override
public SQLXML createSQLXML() throws SQLException {
return null;
}
@Override
public boolean isValid(int timeout) throws SQLException {
return this.mc.getDB(_schema) != null;
}
@Override
public void setClientInfo(String name, String value)
throws SQLClientInfoException {
this._clientInfo.put(name, value);
}
@Override
public void setClientInfo(Properties properties)
throws SQLClientInfoException {
this._clientInfo = properties;
}
@Override
public String getClientInfo(String name) throws SQLException {
// 返回通过名称指定的客户端信息属性的值。
return (String)this._clientInfo.get(name);
}
@Override
public Properties getClientInfo() throws SQLException {
// 返回一个列表,它包含驱动程序支持的每个客户端信息属性的名称和当前值。
return this._clientInfo;
}
@Override
public Array createArrayOf(String typeName, Object[] elements)
throws SQLException {
return null;
}
@Override
public Struct createStruct(String typeName, Object[] attributes)
throws SQLException {
return null;
}
@Override
public void setSchema(String schema) throws SQLException {
//this._schema=schema;
}
@Override
public String getSchema() throws SQLException {
return this._schema;
}
@Override
public void abort(Executor executor) throws SQLException {
}
@Override
public void setNetworkTimeout(Executor executor, int milliseconds)
throws SQLException {
}
@Override
public int getNetworkTimeout() throws SQLException {
return 0;
}
}
@@ -0,0 +1,130 @@
package io.mycat.backend.jdbc.mongodb;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.HashMap;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.BasicDBList;
public class MongoData {
private DBCursor cursor;
private long count;
private String table;
private DBObject groupby;
private HashMap<String,Integer> map = new HashMap<String,Integer>();
private boolean type=false;
public MongoData(){
this.count=0;
this.cursor=null;
}
public long getCount() {
return this.count;
}
public void setCount(long count) {
this.count=count;
}
public String getTable() {
return this.table;
}
public void setTable(String table) {
this.table=table;
}
public DBObject getGrouyBy() {
return this.groupby;
}
public BasicDBList getGrouyBys() {
if (this.groupby instanceof BasicDBList) {
return (BasicDBList)this.groupby;
}
else {
return null;
}
}
public void setGrouyBy(DBObject gb) {
this.groupby=gb;
this.type=true;
if (gb instanceof BasicDBList) {
Object gb2=((BasicDBList)gb).get(0);
if (gb2 instanceof DBObject) {
for (String field :((DBObject)gb2).keySet()) {
Object val = ((DBObject)gb2).get(field);
setField(field,getObjectToType(val));
}
}
}
}
public static int getObjectToType(Object ob){
if (ob instanceof Integer) {
return Types.INTEGER;
}
else if (ob instanceof Boolean) {
return Types.BOOLEAN;
}
else if (ob instanceof Byte) {
return Types.BIT;
}
else if (ob instanceof Short) {
return Types.INTEGER;
}
else if (ob instanceof Float) {
return Types.FLOAT;
}
else if (ob instanceof Long) {
return Types.BIGINT;
}
else if (ob instanceof Double) {
return Types.DOUBLE;
}
else if (ob instanceof Date) {
return Types.DATE;
}
else if (ob instanceof Time) {
return Types.TIME;
}
else if (ob instanceof Timestamp) {
return Types.TIMESTAMP;
}
else if (ob instanceof String) {
return Types.VARCHAR;
}
else {
return Types.VARCHAR;
}
}
public void setField(String field,int ftype) {
map.put(field, ftype);
}
public HashMap<String,Integer> getFields() {
return this.map;
}
public boolean getType() {
return this.type;
}
public DBCursor getCursor() {
return this.cursor;
}
public DBCursor setCursor(DBCursor cursor) {
return this.cursor=cursor;
}
}
@@ -0,0 +1,112 @@
package io.mycat.backend.jdbc.mongodb;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.DriverPropertyInfo;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.Properties;
import java.util.logging.Logger;
import org.slf4j.LoggerFactory;
import com.mongodb.MongoClientURI;
/**
* 功能详细描述
* @author sohudo[http://blog.csdn.net/wind520]
* @create 2014年12月19日 下午6:50:23
* @version 0.0.1
*/
public class MongoDriver implements Driver
{
private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(MongoDriver.class);
static final String PREFIX = "mongodb://";
private DriverPropertyInfoHelper propertyInfoHelper = new DriverPropertyInfoHelper();
static{
try{
DriverManager.registerDriver(new MongoDriver());
}catch (SQLException e){
LOGGER.error("initError",e);
}
}
@Override
public Connection connect(String url, Properties info) throws SQLException {
MongoClientURI mcu = null;
if ((mcu = parseURL(url, info)) == null) {
return null;
}
MongoConnection result = null;
//System.out.print(info);
try{
result = new MongoConnection(mcu, url);
}catch (Exception e){
throw new SQLException("Unexpected exception: " + e.getMessage(), e);
}
return result;
}
private MongoClientURI parseURL(String url, Properties defaults) {
if (url == null) {
return null;
}
if (!StringUtils.startsWithIgnoreCase(url, PREFIX)) {
return null;
}
//删掉开头的 jdbc:
//url = url.replace(URL_JDBC, "");
try {
//FIXME 判断defaults中的参数,写入URL中?
return new MongoClientURI(url);
} catch (Exception e) {
LOGGER.error("parseURLError",e);
return null;
}
}
@Override
public boolean acceptsURL(String url) throws SQLException {
if (StringUtils.startsWithIgnoreCase(url, PREFIX)) {
return true;
}
return false;
}
@Override
public DriverPropertyInfo[] getPropertyInfo(String url, Properties info)
throws SQLException {
return propertyInfoHelper.getPropertyInfo();
}
@Override
public int getMajorVersion() {
return 1;
}
@Override
public int getMinorVersion() {
return 0;
}
@Override
public boolean jdbcCompliant() {
return true;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException{
return null;
}
}
@@ -0,0 +1,409 @@
package io.mycat.backend.jdbc.mongodb;
import java.io.InputStream;
import java.io.Reader;
import java.math.BigDecimal;
import java.net.URL;
import java.sql.Array;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Date;
import java.sql.NClob;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.Ref;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.RowId;
import java.sql.SQLException;
import java.sql.SQLXML;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
/**
* 功能详细描述
* @author sohudo[http://blog.csdn.net/wind520]
* @create 2014年12月19日 下午6:50:23
* @version 0.0.1
*/
public class MongoPreparedStatement extends MongoStatement implements
PreparedStatement {
final String _sql;
final MongoSQLParser _mongosql;
List _params = new ArrayList();
public MongoPreparedStatement(MongoConnection conn, int type,
int concurrency, int holdability, String sql)
throws MongoSQLException {
super(conn, type, concurrency, holdability);
this._sql = sql;
this._mongosql = new MongoSQLParser(conn.getDB(), sql);
}
@Override
public ResultSet executeQuery() throws SQLException {
return null;
}
@Override
public int executeUpdate() throws SQLException {
this._mongosql.setParams(this._params);
return this._mongosql.executeUpdate();
}
public void setValue(int idx, Object o) {
while (this._params.size() <= idx) {
this._params.add(null);
}
this._params.set(idx, o);
}
@Override
public void setNull(int parameterIndex, int sqlType) throws SQLException {
}
@Override
public void setBoolean(int parameterIndex, boolean x) throws SQLException {
setValue(parameterIndex, Boolean.valueOf(x));
}
@Override
public void setByte(int parameterIndex, byte x) throws SQLException {
setValue(parameterIndex, Byte.valueOf(x));
}
@Override
public void setShort(int parameterIndex, short x) throws SQLException {
setValue(parameterIndex, Short.valueOf(x));
}
@Override
public void setInt(int parameterIndex, int x) throws SQLException {
setValue(parameterIndex, Integer.valueOf(x));
}
@Override
public void setLong(int parameterIndex, long x) throws SQLException {
setValue(parameterIndex, Long.valueOf(x));
}
@Override
public void setFloat(int parameterIndex, float x) throws SQLException {
setValue(parameterIndex, Float.valueOf(x));
}
@Override
public void setDouble(int parameterIndex, double x) throws SQLException {
setValue(parameterIndex, Double.valueOf(x));
}
@Override
public void setBigDecimal(int parameterIndex, BigDecimal x)
throws SQLException {
setValue(parameterIndex, x);
}
@Override
public void setString(int parameterIndex, String x) throws SQLException {
setValue(parameterIndex, x);
}
@Override
public void setBytes(int parameterIndex, byte[] x) throws SQLException {
setValue(parameterIndex, x);
}
@Override
public void setDate(int parameterIndex, Date x) throws SQLException {
setValue(parameterIndex, x);
}
@Override
public void setTime(int parameterIndex, Time x) throws SQLException {
setValue(parameterIndex, x);
}
@Override
public void setTimestamp(int parameterIndex, Timestamp x)
throws SQLException {
setValue(parameterIndex, x);
}
@Override
public void setAsciiStream(int parameterIndex, InputStream x, int length)
throws SQLException {
}
@Override
public void setUnicodeStream(int parameterIndex, InputStream x, int length)
throws SQLException {
}
@Override
public void setBinaryStream(int parameterIndex, InputStream x, int length)
throws SQLException {
}
@Override
public void clearParameters() throws SQLException {
}
@Override
public void setObject(int parameterIndex, Object x, int targetSqlType)
throws SQLException {
}
@Override
public void setObject(int parameterIndex, Object x) throws SQLException {
setValue(parameterIndex,x);
}
@Override
public boolean execute() throws SQLException {
return false;
}
@Override
public void addBatch() throws SQLException {
}
@Override
public void setCharacterStream(int parameterIndex, Reader reader, int length)
throws SQLException {
}
@Override
public void setRef(int parameterIndex, Ref x) throws SQLException {
}
@Override
public void setBlob(int parameterIndex, Blob x) throws SQLException {
}
@Override
public void setClob(int parameterIndex, Clob x) throws SQLException {
}
@Override
public void setArray(int parameterIndex, Array x) throws SQLException {
}
@Override
public ResultSetMetaData getMetaData() throws SQLException {
return null;
}
@Override
public void setDate(int parameterIndex, Date x, Calendar cal)
throws SQLException {
}
@Override
public void setTime(int parameterIndex, Time x, Calendar cal)
throws SQLException {
}
@Override
public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal)
throws SQLException {
}
@Override
public void setNull(int parameterIndex, int sqlType, String typeName)
throws SQLException {
}
@Override
public void setURL(int parameterIndex, URL x) throws SQLException {
}
@Override
public ParameterMetaData getParameterMetaData() throws SQLException {
return null;
}
@Override
public void setRowId(int parameterIndex, RowId x) throws SQLException {
}
@Override
public void setNString(int parameterIndex, String value)
throws SQLException {
}
@Override
public void setNCharacterStream(int parameterIndex, Reader value,
long length) throws SQLException {
}
@Override
public void setNClob(int parameterIndex, NClob value) throws SQLException {
}
@Override
public void setClob(int parameterIndex, Reader reader, long length)
throws SQLException {
}
@Override
public void setBlob(int parameterIndex, InputStream inputStream, long length)
throws SQLException {
}
@Override
public void setNClob(int parameterIndex, Reader reader, long length)
throws SQLException {
}
@Override
public void setSQLXML(int parameterIndex, SQLXML xmlObject)
throws SQLException {
}
@Override
public void setObject(int parameterIndex, Object x, int targetSqlType,
int scaleOrLength) throws SQLException {
}
@Override
public void setAsciiStream(int parameterIndex, InputStream x, long length)
throws SQLException {
}
@Override
public void setBinaryStream(int parameterIndex, InputStream x, long length)
throws SQLException {
}
@Override
public void setCharacterStream(int parameterIndex, Reader reader,
long length) throws SQLException {
}
@Override
public void setAsciiStream(int parameterIndex, InputStream x)
throws SQLException {
}
@Override
public void setBinaryStream(int parameterIndex, InputStream x)
throws SQLException {
}
@Override
public void setCharacterStream(int parameterIndex, Reader reader)
throws SQLException {
}
@Override
public void setNCharacterStream(int parameterIndex, Reader value)
throws SQLException {
}
@Override
public void setClob(int parameterIndex, Reader reader) throws SQLException {
}
@Override
public void setBlob(int parameterIndex, InputStream inputStream)
throws SQLException {
}
@Override
public void setNClob(int parameterIndex, Reader reader) throws SQLException {
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,189 @@
package io.mycat.backend.jdbc.mongodb;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Types;
//import java.util.Arrays;
import java.util.Set;
/**
* 功能详细描述
* @author sohudo[http://blog.csdn.net/wind520]
* @create 2014年12月19日 下午6:50:23
* @version 0.0.1
*/
public class MongoResultSetMetaData implements ResultSetMetaData {
private String[] keySet ;
private int[] keytype ;
private String _schema;
private String _table;
/*
public MongoResultSetMetaData(Set<String> keySet,String schema) {
super();
this.keySet = new String[keySet.size()];
this.keySet = keySet.toArray(this.keySet);
this._schema = schema;
}
*/
public MongoResultSetMetaData(String[] select,int [] ftype,String schema,String table) {
super();
this.keySet = select;
this.keytype=ftype;
this._schema = schema;
this._table =table;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
@Override
public int getColumnCount() throws SQLException {
if (keySet==null) {
return 0;
}
else {
return keySet.length;
}
}
@Override
public boolean isAutoIncrement(int column) throws SQLException {
// 是否为自动编号的字段
return false;
}
@Override
public boolean isCaseSensitive(int column) throws SQLException {
//指示列的大小写是否有关系
return true;
}
@Override
public boolean isSearchable(int column) throws SQLException {
//指示是否可以在 where 子句中使用指定的列
return true;
}
@Override
public boolean isCurrency(int column) throws SQLException {
// 指示指定的列是否是一个哈希代码值
return false;
}
@Override
public int isNullable(int column) throws SQLException {
// 指示指定列中的值是否可以为 null。
return 0;
}
@Override
public boolean isSigned(int column) throws SQLException {
// 指示指定列中的值是否带正负号
return false;
}
@Override
public int getColumnDisplaySize(int column) throws SQLException {
return 50;
}
@Override
public String getColumnLabel(int column) throws SQLException {
return keySet[column-1];
}
@Override
public String getColumnName(int column) throws SQLException {
return keySet[column-1];
}
@Override
public String getSchemaName(int column) throws SQLException {
return this._schema;
}
@Override
public int getPrecision(int column) throws SQLException {
//获取指定列的指定列宽
return 0;
}
@Override
public int getScale(int column) throws SQLException {
// 检索指定参数的小数点右边的位数。
return 0;
}
@Override
public String getTableName(int column) throws SQLException {
return this._table;
}
@Override
public String getCatalogName(int column) throws SQLException {
return this._schema;
}
@Override
public int getColumnType(int column) throws SQLException {
// 字段类型
return keytype[column-1];//Types.VARCHAR;
}
@Override
public String getColumnTypeName(int column) throws SQLException {
// 数据库特定的类型名称
switch (keytype[column-1]){
case Types.INTEGER: return "INTEGER";
case Types.BOOLEAN: return "BOOLEAN";
case Types.BIT: return "BITT";
case Types.FLOAT: return "FLOAT";
case Types.BIGINT: return "BIGINT";
case Types.DOUBLE: return "DOUBLE";
case Types.DATE: return "DATE";
case Types.TIME: return "TIME";
case Types.TIMESTAMP: return "TIMESTAMP";
default: return "varchar";
}
}
@Override
public boolean isReadOnly(int column) throws SQLException {
//指示指定的列是否明确不可写入
return false;
}
@Override
public boolean isWritable(int column) throws SQLException {
return false;
}
@Override
public boolean isDefinitelyWritable(int column) throws SQLException {
return false;
}
@Override
public String getColumnClassName(int column) throws SQLException {
// 如果调用方法 ResultSet.getObject 从列中获取值,则返回构造其实例的 Java 类的完全限定名称
return "Object";
}
}
@@ -0,0 +1,22 @@
package io.mycat.backend.jdbc.mongodb;
import java.sql.SQLException;
@SuppressWarnings("serial")
public class MongoSQLException extends SQLException
{
public MongoSQLException(String msg)
{
super(msg);
}
public static class ErrorSQL extends MongoSQLException
{
ErrorSQL(String sql)
{
super(sql);
}
}
}
@@ -0,0 +1,438 @@
package io.mycat.backend.jdbc.mongodb;
import java.sql.Types;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLOrderingSpecification;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock;
import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;
import com.alibaba.druid.sql.ast.statement.*;
import com.alibaba.druid.sql.ast.expr.*;
import com.alibaba.druid.sql.ast.*;
/**
* 功能详细描述
* @author sohudo[http://blog.csdn.net/wind520]
* @create 2014年12月19日 下午6:50:23
* @version 0.0.1
*/
public class MongoSQLParser {
private static final Logger LOGGER = LoggerFactory.getLogger(MongoSQLParser.class);
private final DB _db;
// private final String _sql;
private final SQLStatement statement;
private List _params;
private int _pos;
public MongoSQLParser(DB db, String sql) throws MongoSQLException
{
this._db = db;
// this._sql = sql;
this.statement = parser(sql);
}
public SQLStatement parser(String s) throws MongoSQLException
{
s = s.trim();
try
{
MySqlStatementParser parser = new MySqlStatementParser(s);
return parser.parseStatement();
}
catch (Exception e)
{
LOGGER.error("MongoSQLParser.parserError", e);
}
throw new MongoSQLException.ErrorSQL(s);
}
public void setParams(List params)
{
this._pos = 1;
this._params = params;
}
public MongoData query() throws MongoSQLException{
if (!(statement instanceof SQLSelectStatement)) {
//return null;
throw new IllegalArgumentException("not a query sql statement");
}
MongoData mongo=new MongoData();
DBCursor c=null;
SQLSelectStatement selectStmt = (SQLSelectStatement)statement;
SQLSelectQuery sqlSelectQuery =selectStmt.getSelect().getQuery();
int icount=0;
if(sqlSelectQuery instanceof MySqlSelectQueryBlock) {
MySqlSelectQueryBlock mysqlSelectQuery = (MySqlSelectQueryBlock)selectStmt.getSelect().getQuery();
BasicDBObject fields = new BasicDBObject();
//显示的字段
for(SQLSelectItem item : mysqlSelectQuery.getSelectList()) {
//System.out.println(item.toString());
if (!(item.getExpr() instanceof SQLAllColumnExpr)) {
if (item.getExpr() instanceof SQLAggregateExpr) {
SQLAggregateExpr expr =(SQLAggregateExpr)item.getExpr();
if (expr.getMethodName().equals("COUNT")) {
icount=1;
mongo.setField(getExprFieldName(expr), Types.BIGINT);
}
fields.put(getExprFieldName(expr), Integer.valueOf(1));
}
else {
fields.put(getFieldName(item), Integer.valueOf(1));
}
}
}
//表名
SQLTableSource table=mysqlSelectQuery.getFrom();
DBCollection coll =this._db.getCollection(table.toString());
mongo.setTable(table.toString());
SQLExpr expr=mysqlSelectQuery.getWhere();
DBObject query = parserWhere(expr);
//System.out.println(query);
SQLSelectGroupByClause groupby=mysqlSelectQuery.getGroupBy();
BasicDBObject gbkey = new BasicDBObject();
if (groupby!=null) {
for (SQLExpr gbexpr:groupby.getItems()){
if (gbexpr instanceof SQLIdentifierExpr) {
String name=((SQLIdentifierExpr) gbexpr).getName();
gbkey.put(name, Integer.valueOf(1));
}
}
icount=2;
}
int limitoff=0;
int limitnum=0;
if (mysqlSelectQuery.getLimit()!=null) {
limitoff=getSQLExprToInt(mysqlSelectQuery.getLimit().getOffset());
limitnum=getSQLExprToInt(mysqlSelectQuery.getLimit().getRowCount());
}
if (icount==1) {
mongo.setCount(coll.count(query));
}
else if (icount==2){
BasicDBObject initial = new BasicDBObject();
initial.put("num", 0);
String reduce="function (obj, prev) { "
+" prev.num++}";
mongo.setGrouyBy(coll.group(gbkey, query, initial, reduce));
}
else {
if ((limitoff>0) || (limitnum>0)) {
c = coll.find(query, fields).skip(limitoff).limit(limitnum);
}
else {
c = coll.find(query, fields);
}
SQLOrderBy orderby=mysqlSelectQuery.getOrderBy();
if (orderby != null ){
BasicDBObject order = new BasicDBObject();
for (int i = 0; i < orderby.getItems().size(); i++)
{
SQLSelectOrderByItem orderitem = orderby.getItems().get(i);
order.put(orderitem.getExpr().toString(), Integer.valueOf(getSQLExprToAsc(orderitem.getType())));
}
c.sort(order);
// System.out.println(order);
}
}
mongo.setCursor(c);
}
return mongo;
}
public int executeUpdate() throws MongoSQLException {
if (statement instanceof SQLInsertStatement) {
return InsertData((SQLInsertStatement)statement);
}
if (statement instanceof SQLUpdateStatement) {
return UpData((SQLUpdateStatement)statement);
}
if (statement instanceof SQLDropTableStatement) {
return dropTable((SQLDropTableStatement)statement);
}
if (statement instanceof SQLDeleteStatement) {
return DeleteDate((SQLDeleteStatement)statement);
}
if (statement instanceof SQLCreateTableStatement) {
return 1;
}
return 1;
}
private int InsertData(SQLInsertStatement state) {
if (state.getValues().getValues().size() ==0 ){
throw new RuntimeException("number of columns error");
}
if (state.getValues().getValues().size() != state.getColumns().size()){
throw new RuntimeException("number of values and columns have to match");
}
SQLTableSource table=state.getTableSource();
BasicDBObject o = new BasicDBObject();
int i=0;
for(SQLExpr col : state.getColumns()) {
o.put(getFieldName2(col), getExpValue(state.getValues().getValues().get(i)));
i++;
}
DBCollection coll =this._db.getCollection(table.toString());
coll.insert(new DBObject[] { o });
return 1;
}
private int UpData(SQLUpdateStatement state) {
SQLTableSource table=state.getTableSource();
DBCollection coll =this._db.getCollection(table.toString());
SQLExpr expr=state.getWhere();
DBObject query = parserWhere(expr);
BasicDBObject set = new BasicDBObject();
for(SQLUpdateSetItem col : state.getItems()){
set.put(getFieldName2(col.getColumn()), getExpValue(col.getValue()));
}
DBObject mod = new BasicDBObject("$set", set);
coll.updateMulti(query, mod);
//System.out.println("changs count:"+coll.getStats().size());
return 1;
}
private int DeleteDate(SQLDeleteStatement state) {
SQLTableSource table=state.getTableSource();
DBCollection coll =this._db.getCollection(table.toString());
SQLExpr expr=state.getWhere();
if (expr==null) {
throw new RuntimeException("not where of sql");
}
DBObject query = parserWhere(expr);
coll.remove(query);
return 1;
}
private int dropTable(SQLDropTableStatement state) {
for (SQLTableSource table : state.getTableSources()){
DBCollection coll =this._db.getCollection(table.toString());
coll.drop();
}
return 1;
}
private int getSQLExprToInt(SQLExpr expr){
if (expr instanceof SQLIntegerExpr){
return ((SQLIntegerExpr)expr).getNumber().intValue();
}
return 0;
}
private int getSQLExprToAsc(SQLOrderingSpecification ASC){
if (ASC==null ) {
return 1;
}
if (ASC==SQLOrderingSpecification.DESC){
return -1;
}
else {
return 1;
}
}
public String remove(String resource,char ch)
{
StringBuffer buffer=new StringBuffer();
int position=0;
char currentChar;
while(position<resource.length())
{
currentChar=resource.charAt(position++);
if(currentChar!=ch) {
buffer.append(currentChar);
}
}
return buffer.toString();
}
private Object getExpValue(SQLExpr expr){
if (expr instanceof SQLIntegerExpr){
return ((SQLIntegerExpr)expr).getNumber().intValue();
}
if (expr instanceof SQLNumberExpr){
return ((SQLNumberExpr)expr).getNumber().doubleValue();
}
if (expr instanceof SQLCharExpr){
String va=((SQLCharExpr)expr).toString();
return remove(va,'\'');
}
if (expr instanceof SQLBooleanExpr){
return ((SQLBooleanExpr)expr).getValue();
}
if (expr instanceof SQLNullExpr){
return null;
}
if (expr instanceof SQLVariantRefExpr) {
return this._params.get(this._pos++);
}
return expr;
}
private String getExprFieldName(SQLAggregateExpr expr){
String field="";
for (SQLExpr item :expr.getArguments()){
field+=item.toString();
}
return expr.getMethodName()+"("+field+")";
}
private String getFieldName2(SQLExpr item){
return item.toString();
}
private String getFieldName(SQLSelectItem item){
return item.toString();
}
private DBObject parserWhere(SQLExpr expr){
BasicDBObject o = new BasicDBObject();
parserWhere(expr,o);
return o;
}
private void parserDBObject(BasicDBObject ob,String akey, String aop,Object aval){
boolean isok=false;
if (!(ob.keySet().isEmpty())) {
for (String field : ob.keySet()) {
if (akey.equals(field)){
Object val = ob.get(field);
if (val instanceof BasicDBObject) {
((BasicDBObject) val).put(aop, aval);
ob.put(field, (BasicDBObject) val);
isok=true;
break;
} else if (val instanceof BasicDBList) {
// newobj.put(field, ((BasicDBList)val).copy());
}
}
}
}
if (isok==false) {
BasicDBObject xo = new BasicDBObject();
xo.put(aop, aval);
ob.put(akey,xo);
}
}
@SuppressWarnings("unused")
private void opSQLExpr(SQLBinaryOpExpr expr,BasicDBObject o) {
SQLExpr exprL=expr.getLeft();
if (!(exprL instanceof SQLBinaryOpExpr))
{
if (expr.getOperator().getName().equals("=")) {
o.put(exprL.toString(), getExpValue(expr.getRight()));
}
else {
//BasicDBObject xo = new BasicDBObject();
String op="";
if (expr.getOperator().getName().equals("<")) {
op = "$lt";
}
if (expr.getOperator().getName().equals("<=")) {
op = "$lte";
}
if (expr.getOperator().getName().equals(">")) {
op = "$gt";
}
if (expr.getOperator().getName().equals(">=")) {
op = "$gte";
}
if (expr.getOperator().getName().equals("!=")) {
op = "$ne";
}
if (expr.getOperator().getName().equals("<>")) {
op = "$ne";
}
//xo.put(op, getExpValue(expr.getRight()));
// o.put(exprL.toString(),xo);
parserDBObject(o,exprL.toString(),op, getExpValue(expr.getRight()));
}
}
}
private void parserWhere(SQLExpr aexpr,BasicDBObject o){
if(aexpr instanceof SQLBinaryOpExpr){
SQLBinaryOpExpr expr=(SQLBinaryOpExpr)aexpr;
SQLExpr exprL=expr.getLeft();
if (!(exprL instanceof SQLBinaryOpExpr))
{
//opSQLExpr((SQLBinaryOpExpr)aexpr,o);
if (expr.getOperator().getName().equals("=")) {
o.put(exprL.toString(), getExpValue(expr.getRight()));
}
else {
String op="";
if (expr.getOperator().getName().equals("<")) {
op = "$lt";
}
if (expr.getOperator().getName().equals("<=")) {
op = "$lte";
}
if (expr.getOperator().getName().equals(">")) {
op = "$gt";
}
if (expr.getOperator().getName().equals(">=")) {
op = "$gte";
}
if (expr.getOperator().getName().equals("!=")) {
op = "$ne";
}
if (expr.getOperator().getName().equals("<>")) {
op = "$ne";
}
parserDBObject(o,exprL.toString(),op, getExpValue(expr.getRight()));
}
}
else {
if (expr.getOperator().getName().equals("AND")) {
parserWhere(exprL,o);
parserWhere(expr.getRight(),o);
}
else if (expr.getOperator().getName().equals("OR")) {
orWhere(exprL,expr.getRight(),o);
}
else {
throw new RuntimeException("Can't identify the operation of of where");
}
}
}
}
private void orWhere(SQLExpr exprL,SQLExpr exprR ,BasicDBObject ob){
BasicDBObject xo = new BasicDBObject();
BasicDBObject yo = new BasicDBObject();
parserWhere(exprL,xo);
parserWhere(exprR,yo);
ob.put("$or",new Object[]{xo,yo});
}
}
@@ -0,0 +1,330 @@
package io.mycat.backend.jdbc.mongodb;
import com.mongodb.DBCursor;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;
/**
* 功能详细描述
* @author sohudo[http://blog.csdn.net/wind520]
* @create 2014年12月19日 下午6:50:23
* @version 0.0.1
*/
public class MongoStatement implements Statement
{
private MongoConnection _conn;
private final int _type;
private final int _concurrency;
private final int _holdability;
private int _fetchSize = 0;
//int _maxRows = 0;
private MongoResultSet _last;
public MongoStatement(MongoConnection conn, int type, int concurrency, int holdability)
{
this._conn = conn;
this._type = type;
this._concurrency = concurrency;
this._holdability = holdability;
if (this._type != 0) {
throw new UnsupportedOperationException("type not supported yet");
}
if (this._concurrency != 0) {
throw new UnsupportedOperationException("concurrency not supported yet");
}
if (this._holdability != 0) {
throw new UnsupportedOperationException("holdability not supported yet");
}
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
throw new UnsupportedOperationException();//return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
@Override
public ResultSet executeQuery(String sql) throws SQLException {
MongoData mongo= new MongoSQLParser(this._conn.getDB(), sql).query();
if ((this._fetchSize > 0)
&& (mongo.getCursor()!=null)) {
//设置每次网络请求的最大记录数
mongo.getCursor().batchSize(this._fetchSize);
}
/*
if (this._maxRows > 0)
{
cursor.limit(this._maxRows);
}
*/
this._last = new MongoResultSet(mongo,this._conn.getSchema());
return this._last;
}
@Override
public int executeUpdate(String sql) throws SQLException {
// 执行更新语句
return new MongoSQLParser(this._conn.getDB(), sql).executeUpdate();
}
@Override
public void close() throws SQLException {
this._conn = null;
}
@Override
public int getMaxFieldSize() throws SQLException {
// 获取可以为此 Statement 对象所生成 ResultSet 对象中的字符和二进制列值返回的最大字节数。
return 0;//this._fetchSize;
}
@Override
public void setMaxFieldSize(int max) throws SQLException {
//this._fetchSize=max;
}
@Override
public int getMaxRows() throws SQLException {
// 获取由此 Statement 对象生成的 ResultSet 对象可以包含的最大行数。
return 0;//this._maxRows;
}
@Override
public void setMaxRows(int max) throws SQLException {
//this._maxRows = max;
}
@Override
public void setEscapeProcessing(boolean enable) throws SQLException {
// 将转义处理设置为开或关。
}
@Override
public int getQueryTimeout() throws SQLException {
return 0;
}
@Override
public void setQueryTimeout(int seconds) throws SQLException {
// Statement 对象执行的秒数设置,超时设置。
}
@Override
public void cancel() throws SQLException {
}
@Override
public SQLWarning getWarnings() throws SQLException {
return null;
}
@Override
public void clearWarnings() throws SQLException {
}
@Override
public void setCursorName(String name) throws SQLException {
// 将 SQL 指针名称设置为给定的 String,后续 Statement 对象的 execute 方法将使用此字符串。
}
@Override
public boolean execute(String sql) throws SQLException {
return false;
}
@Override
public ResultSet getResultSet() throws SQLException {
return this._last;
}
@Override
public int getUpdateCount() throws SQLException {
// 记录变更的数量,ResultSet 对象或没有更多结果,则返回 -1
return 0;
}
@Override
public boolean getMoreResults() throws SQLException {
// 移动到此 Statement 对象的下一个结果,如果其为 ResultSet 对象,则返回 true,并隐式关闭利用方法 getResultSet 获取的所有当前 ResultSet 对象。
return false;
}
@Override
public void setFetchDirection(int direction) throws SQLException {
// 此 Statement 对象创建的 ResultSet 对象中将按该方向处理行。
}
@Override
public int getFetchDirection() throws SQLException {
return 0;
}
@Override
public void setFetchSize(int rows) throws SQLException {
// 获取结果集合的行数,该数是根据此 Statement 对象生成的 ResultSet 对象的默认获取大小。
this._fetchSize=rows;
}
@Override
public int getFetchSize() throws SQLException {
// 获取结果集合的行数,该数是根据此 Statement 对象生成的 ResultSet 对象的默认获取大小。
return this._fetchSize;
}
@Override
public int getResultSetConcurrency() throws SQLException {
// 对象生成的 ResultSet 对象的结果集合并发性
return 0;
}
@Override
public int getResultSetType() throws SQLException {
// 对象生成的 ResultSet 对象的结果集合类型。
return 0;
}
@Override
public void addBatch(String sql) throws SQLException {
// 新增批处理
throw new UnsupportedOperationException("batch not supported");
}
@Override
public void clearBatch() throws SQLException {
}
@Override
public int[] executeBatch() throws SQLException {
// 将一批命令提交给数据库来执行,如果全部命令执行成功,则返回更新计数组成的数组。
return null;
}
@Override
public Connection getConnection() throws SQLException {
return this._conn;
}
@Override
public boolean getMoreResults(int current) throws SQLException {
// 将此 Statement 对象移动到下一个结果,根据给定标志指定的指令处理所有当前 ResultSet 对象;如果下一个结果为 ResultSet 对象,则返回 true。
return false;
}
@Override
public ResultSet getGeneratedKeys() throws SQLException {
// 获取由于执行此 Statement 对象而创建的所有自动生成的键。
return null;
}
@Override
public int executeUpdate(String sql, int autoGeneratedKeys)
throws SQLException {
return 0;
//throw new RuntimeException("executeUpdate not done");
}
@Override
public int executeUpdate(String sql, int[] columnIndexes)
throws SQLException {
return 0;
//throw new RuntimeException("executeUpdate not done");
}
@Override
public int executeUpdate(String sql, String[] columnNames)
throws SQLException {
return 0;
//throw new RuntimeException("executeUpdate not done");
}
@Override
public boolean execute(String sql, int autoGeneratedKeys)
throws SQLException {
return false;
}
@Override
public boolean execute(String sql, int[] columnIndexes) throws SQLException {
return false;
}
@Override
public boolean execute(String sql, String[] columnNames)
throws SQLException {
return false;
}
@Override
public int getResultSetHoldability() throws SQLException {
return 0;
}
@Override
public boolean isClosed() throws SQLException {
return this._conn == null;
}
@Override
public void setPoolable(boolean poolable) throws SQLException {
// 请求将 Statement 池化或非池化
}
@Override
public boolean isPoolable() throws SQLException {
return false;
}
@Override
public void closeOnCompletion() throws SQLException {
}
@Override
public boolean isCloseOnCompletion() throws SQLException {
return false;
}
}
@@ -0,0 +1,16 @@
package io.mycat.backend.jdbc.mongodb;
public class StringUtils {
public static boolean startsWithIgnoreCase(String searchIn, int startAt,
String searchFor) {
return searchIn.regionMatches(true, startAt, searchFor, 0, searchFor
.length());
}
public static boolean startsWithIgnoreCase(String searchIn, String searchFor) {
return startsWithIgnoreCase(searchIn, 0, searchFor);
}
}
@@ -0,0 +1,70 @@
package io.mycat.backend.jdbc.sequoiadb;
import java.sql.DriverPropertyInfo;
import java.util.ArrayList;
public class DriverPropertyInfoHelper{
public static final String AUTO_CONNECT_RETRY = "autoConnectRetry";
public static final String CONNECTIONS_PER_HOST = "connecionsPerHost";
public static final String CONNECT_TIMEOUT = "connectTimeout";
public static final String CURSOR_FINALIZER_ENABLED = "cursorFinalizerEnabled";
public static final String MAX_AUTO_CONNECT_RETRY_TIME = "maxAutoConnectRetryTime";
public static final String READ_PREFERENCE = "readPreference";
public static final String SOCKET_TIMEOUT = "socketTimeout";
public DriverPropertyInfo[] getPropertyInfo()
{
ArrayList<DriverPropertyInfo> propInfos = new ArrayList<DriverPropertyInfo>();
addPropInfo(
propInfos,
AUTO_CONNECT_RETRY,
"false",
"If true, the driver will keep trying to connect to the same server in case that the socket "
+ "cannot be established. There is maximum amount of time to keep retrying, which is 15s by "
+ "default.", null);
addPropInfo(propInfos, CONNECTIONS_PER_HOST, "10", "The maximum number of connections allowed per "
+ "host for this Mongo instance. Those connections will be kept in a pool when idle.", null);
addPropInfo(propInfos, CONNECT_TIMEOUT, "10000", "The connection timeout in milliseconds. ", null);
addPropInfo(propInfos, CURSOR_FINALIZER_ENABLED, "true", "Sets whether there is a a finalize "
+ "method created that cleans up instances of DBCursor that the client does not close.",
null);
addPropInfo(propInfos, MAX_AUTO_CONNECT_RETRY_TIME, "0",
"The maximum amount of time in MS to spend retrying to open connection to the same server."
+ "Default is 0, which means to use the default 15s if autoConnectRetry is on.", null);
addPropInfo(propInfos, READ_PREFERENCE, "primary",
"represents preferred replica set members to which a query or command can be sent", new String[] {
"primary", "primary preferred", "secondary", "secondary preferred", "nearest" });
addPropInfo(propInfos, SOCKET_TIMEOUT, "0", "The socket timeout in milliseconds It is used for "
+ "I/O socket read and write operations "
+ "Socket.setSoTimeout(int) Default is 0 and means no timeout.", null);
return propInfos.toArray(new DriverPropertyInfo[propInfos.size()]);
}
private void addPropInfo(final ArrayList<DriverPropertyInfo> propInfos, final String propName,
final String defaultVal, final String description, final String[] choices)
{
DriverPropertyInfo newProp = new DriverPropertyInfo(propName, defaultVal);
newProp.description = description;
if (choices != null)
{
newProp.choices = choices;
}
propInfos.add(newProp);
}
}
@@ -0,0 +1,413 @@
package io.mycat.backend.jdbc.sequoiadb;
import java.net.UnknownHostException;
import java.sql.Array;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.NClob;
import java.sql.PreparedStatement;
import java.sql.SQLClientInfoException;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Savepoint;
import java.sql.Statement;
import java.sql.Struct;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;
import com.sequoiadb.base.Sequoiadb;
import com.sequoiadb.base.CollectionSpace;
import com.sequoiadb.exception.BaseException;
/**
* 功能详细描述
* @author sohudo[http://blog.csdn.net/wind520]
* @create 2014年12月19日 下午6:50:23
* @version 0.0.1
*/
public class SequoiaConnection implements Connection {
//private String url = null;
private Sequoiadb mc = null;
private boolean isClosed = false;
private String _schema;
private Properties _clientInfo;
public SequoiaConnection(String url, String db) throws UnknownHostException {
// this.url = url;
this._schema = db;
try {
mc = new Sequoiadb(url, "", "");
} catch (BaseException e) {
throw new IllegalArgumentException("Failed to connect to database: " + url
+ ", error description" + e.getErrorType());
}
}
public CollectionSpace getDB() {
if (this._schema!=null) {
if (mc.isCollectionSpaceExist(this._schema)) {
return this.mc.getCollectionSpace(this._schema);
}
else {
return this.mc.createCollectionSpace(this._schema);
}
}
else {
return null;
}
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
@Override
public String nativeSQL(String sql) throws SQLException {
return sql;
}
@Override
public void setAutoCommit(boolean autoCommit) throws SQLException {
if (!autoCommit) {
throw new RuntimeException("autoCommit has to be on");
}
}
@Override
public boolean getAutoCommit() throws SQLException {
return true;//return false;
}
@Override
public void commit() throws SQLException {
}
@Override
public void rollback() throws SQLException {
//throw new RuntimeException("can't rollback");
}
@Override
public void close() throws SQLException {
this.mc=null;
isClosed=true;
}
@Override
public boolean isClosed() throws SQLException {
return isClosed;//return false;
}
@Override
public DatabaseMetaData getMetaData() throws SQLException {
// 获取一个 DatabaseMetaData 对象,该对象包含关于此 Connection 对象所连接的数据库的元数据。
return null;
}
@Override
public void setReadOnly(boolean readOnly) throws SQLException {
//if (readOnly)
// throw new RuntimeException("no read only mode");
}
@Override
public boolean isReadOnly() throws SQLException {
// 查询此 Connection 对象是否处于只读模式。
return false;
}
@Override
public void setCatalog(String catalog) throws SQLException {
this._schema=catalog;
}
@Override
public String getCatalog() throws SQLException {
// 获取此 Connection 对象的当前目录名称
return this._schema;
}
@Override
public void setTransactionIsolation(int level) throws SQLException {
//throw new RuntimeException("no TransactionIsolation");
}
@Override
public int getTransactionIsolation() throws SQLException {
return 0;
}
@Override
public SQLWarning getWarnings() throws SQLException {
return null;//throw new RuntimeException("should do get last error");
}
@Override
public void clearWarnings() throws SQLException {
}
@Override
public Map<String, Class<?>> getTypeMap() throws SQLException {
return null;
}
@Override
public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
}
@Override
public void setHoldability(int holdability) throws SQLException {
// 将使用此 Connection 对象创建的 ResultSet 对象的默认可保存性 (holdability) 更改为给定可保存性。
}
@Override
public int getHoldability() throws SQLException {
// 获取使用此 Connection 对象创建的 ResultSet 对象的当前可保存性。
return 0;
}
@Override
public Savepoint setSavepoint() throws SQLException {
return null;//throw new RuntimeException("no savepoints");
}
@Override
public Savepoint setSavepoint(String name) throws SQLException {
return null;
}
@Override
public void rollback(Savepoint savepoint) throws SQLException {
// throw new RuntimeException("can't rollback");
}
@Override
public void releaseSavepoint(Savepoint savepoint) throws SQLException {
}
@Override
public Statement createStatement() throws SQLException {
// 创建一个 Statement 对象来将 SQL 语句发送到数据库。
return createStatement(0, 0, 0);
}
@Override
public Statement createStatement(int resultSetType, int resultSetConcurrency)
throws SQLException {
// 创建一个 Statement 对象,该对象将生成具有给定类型和并发性的 ResultSet 对象。
return createStatement(resultSetType, resultSetConcurrency, 0);
}
@Override
public Statement createStatement(int resultSetType,
int resultSetConcurrency, int resultSetHoldability)
throws SQLException {
// 创建一个 Statement 对象,该对象将生成具有给定类型、并发性和可保存性的 ResultSet 对象。
return new SequoiaStatement(this, resultSetType, resultSetConcurrency, resultSetHoldability);
}
@Override
public CallableStatement prepareCall(String sql) throws SQLException {
return prepareCall(sql, 0, 0, 0);
}
@Override
public CallableStatement prepareCall(String sql, int resultSetType,
int resultSetConcurrency) throws SQLException {
return prepareCall(sql, resultSetType, resultSetConcurrency, 0);
}
@Override
public CallableStatement prepareCall(String sql, int resultSetType,
int resultSetConcurrency, int resultSetHoldability)
throws SQLException {
//return null;
throw new RuntimeException("CallableStatement not supported");
}
@Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
return prepareStatement(sql, 0, 0, 0);
}
@Override
public PreparedStatement prepareStatement(String sql, int resultSetType,
int resultSetConcurrency) throws SQLException {
return prepareStatement(sql, resultSetType, resultSetConcurrency, 0);
}
@Override
public PreparedStatement prepareStatement(String sql, int resultSetType,
int resultSetConcurrency, int resultSetHoldability)
throws SQLException {
return new SequoiaPreparedStatement(this, resultSetType, resultSetConcurrency, resultSetHoldability,sql);
}
@Override
public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys)
throws SQLException {
return null;
}
@Override
public PreparedStatement prepareStatement(String sql, int[] columnIndexes)
throws SQLException {
return null;
}
@Override
public PreparedStatement prepareStatement(String sql, String[] columnNames)
throws SQLException {
return null;
}
@Override
public Clob createClob() throws SQLException {
return null;
}
@Override
public Blob createBlob() throws SQLException {
return null;
}
@Override
public NClob createNClob() throws SQLException {
return null;
}
@Override
public SQLXML createSQLXML() throws SQLException {
return null;
}
@Override
public boolean isValid(int timeout) throws SQLException {
return getDB() != null;
}
@Override
public void setClientInfo(String name, String value)
throws SQLClientInfoException {
this._clientInfo.put(name, value);
}
@Override
public void setClientInfo(Properties properties)
throws SQLClientInfoException {
this._clientInfo = properties;
}
@Override
public String getClientInfo(String name) throws SQLException {
// 返回通过名称指定的客户端信息属性的值。
return (String)this._clientInfo.get(name);
}
@Override
public Properties getClientInfo() throws SQLException {
// 返回一个列表,它包含驱动程序支持的每个客户端信息属性的名称和当前值。
return this._clientInfo;
}
@Override
public Array createArrayOf(String typeName, Object[] elements)
throws SQLException {
return null;
}
@Override
public Struct createStruct(String typeName, Object[] attributes)
throws SQLException {
return null;
}
@Override
public void setSchema(String schema) throws SQLException {
//this._schema=schema;
}
@Override
public String getSchema() throws SQLException {
return this._schema;
}
@Override
public void abort(Executor executor) throws SQLException {
}
@Override
public void setNetworkTimeout(Executor executor, int milliseconds)
throws SQLException {
}
@Override
public int getNetworkTimeout() throws SQLException {
return 0;
}
}
@@ -0,0 +1,131 @@
package io.mycat.backend.jdbc.sequoiadb;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.HashMap;
import org.bson.BSONObject;
import org.bson.BasicBSONObject;
import org.bson.types.BasicBSONList;
import com.sequoiadb.base.DBCursor;
public class SequoiaData {
private DBCursor cursor;
private long count;
private String table;
private BSONObject groupby;
private HashMap<String,Integer> map = new HashMap<String,Integer>();
private boolean type=false;
public SequoiaData(){
this.count=0;
this.cursor=null;
}
public long getCount() {
return this.count;
}
public void setCount(long count) {
this.count=count;
}
public String getTable() {
return this.table;
}
public void setTable(String table) {
this.table=table;
}
public BSONObject getGrouyBy() {
return this.groupby;
}
public BasicBSONList getGrouyBys() {
if (this.groupby instanceof BasicBSONList) {
return (BasicBSONList)this.groupby;
}
else {
return null;
}
}
public void setGrouyBy(BSONObject gb) {
this.groupby=gb;
this.type=true;
if (gb instanceof BasicBSONList) {
Object gb2=((BasicBSONList)gb).get(0);
if (gb2 instanceof BSONObject) {
for (String field :((BSONObject)gb2).keySet()) {
Object val = ((BSONObject)gb2).get(field);
setField(field,getObjectToType(val));
}
}
}
}
public static int getObjectToType(Object ob){
if (ob instanceof Integer) {
return Types.INTEGER;
}
else if (ob instanceof Boolean) {
return Types.BOOLEAN;
}
else if (ob instanceof Byte) {
return Types.BIT;
}
else if (ob instanceof Short) {
return Types.INTEGER;
}
else if (ob instanceof Float) {
return Types.FLOAT;
}
else if (ob instanceof Long) {
return Types.BIGINT;
}
else if (ob instanceof Double) {
return Types.DOUBLE;
}
else if (ob instanceof Date) {
return Types.DATE;
}
else if (ob instanceof Time) {
return Types.TIME;
}
else if (ob instanceof Timestamp) {
return Types.TIMESTAMP;
}
else if (ob instanceof String) {
return Types.VARCHAR;
}
else {
return Types.VARCHAR;
}
}
public void setField(String field,int ftype) {
map.put(field, ftype);
}
public HashMap<String,Integer> getFields() {
return this.map;
}
public boolean getType() {
return this.type;
}
public DBCursor getCursor() {
return this.cursor;
}
public DBCursor setCursor(DBCursor cursor) {
return this.cursor=cursor;
}
}
@@ -0,0 +1,122 @@
package io.mycat.backend.jdbc.sequoiadb;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.DriverPropertyInfo;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.Properties;
import java.util.logging.Logger;
import org.slf4j.LoggerFactory;
/**
* 功能详细描述
* @author sohudo[http://blog.csdn.net/wind520]
* @create 2014年12月19日 下午6:50:23
* @version 0.0.1
*/
public class SequoiaDriver implements Driver
{
private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(SequoiaDriver.class);
static final String PREFIX = "sequoiadb://";
private DriverPropertyInfoHelper propertyInfoHelper = new DriverPropertyInfoHelper();
static{
try{
DriverManager.registerDriver(new SequoiaDriver());
}catch (SQLException e){
LOGGER.error("initError",e);
}
}
@Override
public Connection connect(String url, Properties info) throws SQLException {
if (url == null) {
return null;
}
if (!StringUtils.startsWithIgnoreCase(url, PREFIX)) {
return null;//throw new IllegalArgumentException("uri needs to start with " + PREFIX);//return null;
}
String uri=url;
uri = uri.substring(PREFIX.length());
String serverPart;
String nsPart;
String optionsPart;
{
int idx = uri.lastIndexOf("/");
if (idx < 0) {
if (uri.contains("?")) {
throw new IllegalArgumentException("URI contains options without trailing slash");
}
serverPart = uri;
nsPart = null;
optionsPart = "";
} else {
serverPart = uri.substring(0, idx);
nsPart = uri.substring(idx + 1);
idx = nsPart.indexOf("?");
if (idx >= 0) {
optionsPart = nsPart.substring(idx + 1);
nsPart = nsPart.substring(0, idx);
} else {
optionsPart = "";
}
}
}
SequoiaConnection result = null;
//System.out.print(info);
try{
result = new SequoiaConnection(serverPart, nsPart);
}catch (Exception e){
throw new SQLException("Unexpected exception: " + e.getMessage(), e);
}
return result;
}
@Override
public boolean acceptsURL(String url) throws SQLException {
if (!StringUtils.startsWithIgnoreCase(url, PREFIX)) {
return false;
}
return true;
}
@Override
public DriverPropertyInfo[] getPropertyInfo(String url, Properties info)
throws SQLException {
return propertyInfoHelper.getPropertyInfo();
}
@Override
public int getMajorVersion() {
return 1;
}
@Override
public int getMinorVersion() {
return 0;
}
@Override
public boolean jdbcCompliant() {
return true;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException{
return null;
}
}
@@ -0,0 +1,409 @@
package io.mycat.backend.jdbc.sequoiadb;
import java.io.InputStream;
import java.io.Reader;
import java.math.BigDecimal;
import java.net.URL;
import java.sql.Array;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Date;
import java.sql.NClob;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.Ref;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.RowId;
import java.sql.SQLException;
import java.sql.SQLXML;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
/**
* 功能详细描述
* @author sohudo[http://blog.csdn.net/wind520]
* @create 2014年12月19日 下午6:50:23
* @version 0.0.1
*/
public class SequoiaPreparedStatement extends SequoiaStatement implements
PreparedStatement {
final String _sql;
final SequoiaSQLParser _mongosql;
List _params = new ArrayList();
public SequoiaPreparedStatement(SequoiaConnection conn, int type,
int concurrency, int holdability, String sql)
throws SequoiaSQLException {
super(conn, type, concurrency, holdability);
this._sql = sql;
this._mongosql = new SequoiaSQLParser(conn.getDB(), sql);
}
@Override
public ResultSet executeQuery() throws SQLException {
return null;
}
@Override
public int executeUpdate() throws SQLException {
this._mongosql.setParams(this._params);
return this._mongosql.executeUpdate();
}
public void setValue(int idx, Object o) {
while (this._params.size() <= idx) {
this._params.add(null);
}
this._params.set(idx, o);
}
@Override
public void setNull(int parameterIndex, int sqlType) throws SQLException {
}
@Override
public void setBoolean(int parameterIndex, boolean x) throws SQLException {
setValue(parameterIndex, Boolean.valueOf(x));
}
@Override
public void setByte(int parameterIndex, byte x) throws SQLException {
setValue(parameterIndex, Byte.valueOf(x));
}
@Override
public void setShort(int parameterIndex, short x) throws SQLException {
setValue(parameterIndex, Short.valueOf(x));
}
@Override
public void setInt(int parameterIndex, int x) throws SQLException {
setValue(parameterIndex, Integer.valueOf(x));
}
@Override
public void setLong(int parameterIndex, long x) throws SQLException {
setValue(parameterIndex, Long.valueOf(x));
}
@Override
public void setFloat(int parameterIndex, float x) throws SQLException {
setValue(parameterIndex, Float.valueOf(x));
}
@Override
public void setDouble(int parameterIndex, double x) throws SQLException {
setValue(parameterIndex, Double.valueOf(x));
}
@Override
public void setBigDecimal(int parameterIndex, BigDecimal x)
throws SQLException {
setValue(parameterIndex, x);
}
@Override
public void setString(int parameterIndex, String x) throws SQLException {
setValue(parameterIndex, x);
}
@Override
public void setBytes(int parameterIndex, byte[] x) throws SQLException {
setValue(parameterIndex, x);
}
@Override
public void setDate(int parameterIndex, Date x) throws SQLException {
setValue(parameterIndex, x);
}
@Override
public void setTime(int parameterIndex, Time x) throws SQLException {
setValue(parameterIndex, x);
}
@Override
public void setTimestamp(int parameterIndex, Timestamp x)
throws SQLException {
setValue(parameterIndex, x);
}
@Override
public void setAsciiStream(int parameterIndex, InputStream x, int length)
throws SQLException {
}
@Override
public void setUnicodeStream(int parameterIndex, InputStream x, int length)
throws SQLException {
}
@Override
public void setBinaryStream(int parameterIndex, InputStream x, int length)
throws SQLException {
}
@Override
public void clearParameters() throws SQLException {
}
@Override
public void setObject(int parameterIndex, Object x, int targetSqlType)
throws SQLException {
}
@Override
public void setObject(int parameterIndex, Object x) throws SQLException {
setValue(parameterIndex,x);
}
@Override
public boolean execute() throws SQLException {
return false;
}
@Override
public void addBatch() throws SQLException {
}
@Override
public void setCharacterStream(int parameterIndex, Reader reader, int length)
throws SQLException {
}
@Override
public void setRef(int parameterIndex, Ref x) throws SQLException {
}
@Override
public void setBlob(int parameterIndex, Blob x) throws SQLException {
}
@Override
public void setClob(int parameterIndex, Clob x) throws SQLException {
}
@Override
public void setArray(int parameterIndex, Array x) throws SQLException {
}
@Override
public ResultSetMetaData getMetaData() throws SQLException {
return null;
}
@Override
public void setDate(int parameterIndex, Date x, Calendar cal)
throws SQLException {
}
@Override
public void setTime(int parameterIndex, Time x, Calendar cal)
throws SQLException {
}
@Override
public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal)
throws SQLException {
}
@Override
public void setNull(int parameterIndex, int sqlType, String typeName)
throws SQLException {
}
@Override
public void setURL(int parameterIndex, URL x) throws SQLException {
}
@Override
public ParameterMetaData getParameterMetaData() throws SQLException {
return null;
}
@Override
public void setRowId(int parameterIndex, RowId x) throws SQLException {
}
@Override
public void setNString(int parameterIndex, String value)
throws SQLException {
}
@Override
public void setNCharacterStream(int parameterIndex, Reader value,
long length) throws SQLException {
}
@Override
public void setNClob(int parameterIndex, NClob value) throws SQLException {
}
@Override
public void setClob(int parameterIndex, Reader reader, long length)
throws SQLException {
}
@Override
public void setBlob(int parameterIndex, InputStream inputStream, long length)
throws SQLException {
}
@Override
public void setNClob(int parameterIndex, Reader reader, long length)
throws SQLException {
}
@Override
public void setSQLXML(int parameterIndex, SQLXML xmlObject)
throws SQLException {
}
@Override
public void setObject(int parameterIndex, Object x, int targetSqlType,
int scaleOrLength) throws SQLException {
}
@Override
public void setAsciiStream(int parameterIndex, InputStream x, long length)
throws SQLException {
}
@Override
public void setBinaryStream(int parameterIndex, InputStream x, long length)
throws SQLException {
}
@Override
public void setCharacterStream(int parameterIndex, Reader reader,
long length) throws SQLException {
}
@Override
public void setAsciiStream(int parameterIndex, InputStream x)
throws SQLException {
}
@Override
public void setBinaryStream(int parameterIndex, InputStream x)
throws SQLException {
}
@Override
public void setCharacterStream(int parameterIndex, Reader reader)
throws SQLException {
}
@Override
public void setNCharacterStream(int parameterIndex, Reader value)
throws SQLException {
}
@Override
public void setClob(int parameterIndex, Reader reader) throws SQLException {
}
@Override
public void setBlob(int parameterIndex, InputStream inputStream)
throws SQLException {
}
@Override
public void setNClob(int parameterIndex, Reader reader) throws SQLException {
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,188 @@
package io.mycat.backend.jdbc.sequoiadb;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Types;
/**
* 功能详细描述
* @author sohudo[http://blog.csdn.net/wind520]
* @create 2014年12月19日 下午6:50:23
* @version 0.0.1
*/
public class SequoiaResultSetMetaData implements ResultSetMetaData {
private String[] keySet ;
private int[] keytype ;
private String _schema;
private String _table;
/*
public MongoResultSetMetaData(Set<String> keySet,String schema) {
super();
this.keySet = new String[keySet.size()];
this.keySet = keySet.toArray(this.keySet);
this._schema = schema;
}
*/
public SequoiaResultSetMetaData(String[] select,int [] ftype,String schema,String table) {
super();
this.keySet = select;
this.keytype=ftype;
this._schema = schema;
this._table =table;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
@Override
public int getColumnCount() throws SQLException {
if (keySet==null) {
return 0;
}
else {
return keySet.length;
}
}
@Override
public boolean isAutoIncrement(int column) throws SQLException {
// 是否为自动编号的字段
return false;
}
@Override
public boolean isCaseSensitive(int column) throws SQLException {
//指示列的大小写是否有关系
return true;
}
@Override
public boolean isSearchable(int column) throws SQLException {
//指示是否可以在 where 子句中使用指定的列
return true;
}
@Override
public boolean isCurrency(int column) throws SQLException {
// 指示指定的列是否是一个哈希代码值
return false;
}
@Override
public int isNullable(int column) throws SQLException {
// 指示指定列中的值是否可以为 null。
return 0;
}
@Override
public boolean isSigned(int column) throws SQLException {
// 指示指定列中的值是否带正负号
return false;
}
@Override
public int getColumnDisplaySize(int column) throws SQLException {
return 50;
}
@Override
public String getColumnLabel(int column) throws SQLException {
return keySet[column-1];
}
@Override
public String getColumnName(int column) throws SQLException {
return keySet[column-1];
}
@Override
public String getSchemaName(int column) throws SQLException {
return this._schema;
}
@Override
public int getPrecision(int column) throws SQLException {
//获取指定列的指定列宽
return 0;
}
@Override
public int getScale(int column) throws SQLException {
// 检索指定参数的小数点右边的位数。
return 0;
}
@Override
public String getTableName(int column) throws SQLException {
return this._table;
}
@Override
public String getCatalogName(int column) throws SQLException {
return this._schema;
}
@Override
public int getColumnType(int column) throws SQLException {
// 字段类型
return keytype[column-1];//Types.VARCHAR;
}
@Override
public String getColumnTypeName(int column) throws SQLException {
// 数据库特定的类型名称
switch (keytype[column-1]){
case Types.INTEGER: return "INTEGER";
case Types.BOOLEAN: return "BOOLEAN";
case Types.BIT: return "BITT";
case Types.FLOAT: return "FLOAT";
case Types.BIGINT: return "BIGINT";
case Types.DOUBLE: return "DOUBLE";
case Types.DATE: return "DATE";
case Types.TIME: return "TIME";
case Types.TIMESTAMP: return "TIMESTAMP";
default: return "varchar";
}
}
@Override
public boolean isReadOnly(int column) throws SQLException {
//指示指定的列是否明确不可写入
return false;
}
@Override
public boolean isWritable(int column) throws SQLException {
return false;
}
@Override
public boolean isDefinitelyWritable(int column) throws SQLException {
return false;
}
@Override
public String getColumnClassName(int column) throws SQLException {
// 如果调用方法 ResultSet.getObject 从列中获取值,则返回构造其实例的 Java 类的完全限定名称
return "Object";
}
}
@@ -0,0 +1,22 @@
package io.mycat.backend.jdbc.sequoiadb;
import java.sql.SQLException;
@SuppressWarnings("serial")
public class SequoiaSQLException extends SQLException
{
public SequoiaSQLException(String msg)
{
super(msg);
}
public static class ErrorSQL extends SequoiaSQLException
{
ErrorSQL(String sql)
{
super(sql);
}
}
}
@@ -0,0 +1,451 @@
package io.mycat.backend.jdbc.sequoiadb;
import java.sql.Types;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.sequoiadb.base.CollectionSpace;
import com.sequoiadb.base.DBCollection;
import com.sequoiadb.base.DBCursor;
import org.bson.BSONObject;
import org.bson.BasicBSONObject;
import org.bson.types.BasicBSONList;
import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLOrderingSpecification;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock;
import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;
import com.alibaba.druid.sql.ast.statement.*;
import com.alibaba.druid.sql.ast.expr.*;
import com.alibaba.druid.sql.ast.*;
/**
* 功能详细描述
* @author sohudo[http://blog.csdn.net/wind520]
* @create 2014年12月19日 下午6:50:23
* @version 0.0.1
*/
public class SequoiaSQLParser {
private static final Logger LOGGER = LoggerFactory.getLogger(SequoiaSQLParser.class);
private final CollectionSpace _db;
// private final String _sql;
private final SQLStatement statement;
private List _params;
private int _pos;
public SequoiaSQLParser(CollectionSpace db, String sql) throws SequoiaSQLException
{
this._db = db;
// this._sql = sql;
this.statement = parser(sql);
}
public SQLStatement parser(String s) throws SequoiaSQLException
{
s = s.trim();
try
{
MySqlStatementParser parser = new MySqlStatementParser(s);
return parser.parseStatement();
}
catch (Exception e)
{
LOGGER.error("MongoSQLParser.parserError", e);
}
throw new SequoiaSQLException.ErrorSQL(s);
}
public void setParams(List params)
{
this._pos = 1;
this._params = params;
}
public SequoiaData query() throws SequoiaSQLException{
if (!(statement instanceof SQLSelectStatement)) {
//return null;
throw new IllegalArgumentException("not a query sql statement");
}
SequoiaData mongo=new SequoiaData();
DBCursor c=null;
SQLSelectStatement selectStmt = (SQLSelectStatement)statement;
SQLSelectQuery sqlSelectQuery =selectStmt.getSelect().getQuery();
int icount=0;
if(sqlSelectQuery instanceof MySqlSelectQueryBlock) {
MySqlSelectQueryBlock mysqlSelectQuery = (MySqlSelectQueryBlock)selectStmt.getSelect().getQuery();
BasicBSONObject fields = new BasicBSONObject();
//显示的字段
for(SQLSelectItem item : mysqlSelectQuery.getSelectList()) {
//System.out.println(item.toString());
if (!(item.getExpr() instanceof SQLAllColumnExpr)) {
if (item.getExpr() instanceof SQLAggregateExpr) {
SQLAggregateExpr expr =(SQLAggregateExpr)item.getExpr();
if (expr.getMethodName().equals("COUNT")) {
icount=1;
mongo.setField(getExprFieldName(expr), Types.BIGINT);
}
fields.put(getExprFieldName(expr), Integer.valueOf(1));
}
else {
fields.put(getFieldName(item), Integer.valueOf(1));
}
}
}
//表名
SQLTableSource table=mysqlSelectQuery.getFrom();
DBCollection coll =this._db.getCollection(table.toString());
mongo.setTable(table.toString());
SQLExpr expr=mysqlSelectQuery.getWhere();
BSONObject query = parserWhere(expr);
//System.out.println(query);
SQLSelectGroupByClause groupby=mysqlSelectQuery.getGroupBy();
BasicBSONObject gbkey = new BasicBSONObject();
if (groupby!=null) {
for (SQLExpr gbexpr:groupby.getItems()){
if (gbexpr instanceof SQLIdentifierExpr) {
String name =((SQLIdentifierExpr) gbexpr).getName();
gbkey.put(name, Integer.valueOf(1));
}
}
icount=2;
}
int limitoff=0;
int limitnum=0;
if (mysqlSelectQuery.getLimit()!=null) {
limitoff=getSQLExprToInt(mysqlSelectQuery.getLimit().getOffset());
limitnum=getSQLExprToInt(mysqlSelectQuery.getLimit().getRowCount());
}
SQLOrderBy orderby=mysqlSelectQuery.getOrderBy();
BasicBSONObject order = new BasicBSONObject();
if (orderby != null ){
for (int i = 0; i < orderby.getItems().size(); i++)
{
SQLSelectOrderByItem orderitem = orderby.getItems().get(i);
order.put(orderitem.getExpr().toString(), Integer.valueOf(getSQLExprToAsc(orderitem.getType())));
}
// c.sort(order);
// System.out.println(order);
}
if (icount==1) {
mongo.setCount(coll.getCount(query));
}
else if (icount==2){
BasicBSONObject initial = new BasicBSONObject();
initial.put("num", 0);
String reduce="function (obj, prev) { "
+" prev.num++}";
//mongo.setGrouyBy(coll.group(gbkey, query, initial, reduce));
}
else {
if ((limitoff>0) || (limitnum>0)) {
c = coll.query(query, fields, order,null, limitoff, limitnum);//.skip(limitoff).limit(limitnum);
}
else {
c = coll.query(query, fields, order,null, 0, -1);
}
}
mongo.setCursor(c);
}
return mongo;
}
public int executeUpdate() throws SequoiaSQLException {
if (statement instanceof SQLInsertStatement) {
return InsertData((SQLInsertStatement)statement);
}
if (statement instanceof SQLUpdateStatement) {
return UpData((SQLUpdateStatement)statement);
}
if (statement instanceof SQLDropTableStatement) {
return dropTable((SQLDropTableStatement)statement);
}
if (statement instanceof SQLDeleteStatement) {
return DeleteDate((SQLDeleteStatement)statement);
}
if (statement instanceof SQLCreateTableStatement) {
return createTable((SQLCreateTableStatement)statement);
}
return 1;
}
private int InsertData(SQLInsertStatement state) {
if (state.getValues().getValues().size() ==0 ){
throw new RuntimeException("number of columns error");
}
if (state.getValues().getValues().size() != state.getColumns().size()){
throw new RuntimeException("number of values and columns have to match");
}
SQLTableSource table=state.getTableSource();
BSONObject o = new BasicBSONObject();
int i=0;
for(SQLExpr col : state.getColumns()) {
o.put(getFieldName2(col), getExpValue(state.getValues().getValues().get(i)));
i++;
}
DBCollection coll =this._db.getCollection(table.toString());
//coll.insert(new DBObject[] { o });
coll.insert(o);
return 1;
}
private int UpData(SQLUpdateStatement state) {
SQLTableSource table=state.getTableSource();
DBCollection coll =this._db.getCollection(table.toString());
SQLExpr expr=state.getWhere();
BSONObject query = parserWhere(expr);
BasicBSONObject set = new BasicBSONObject();
for(SQLUpdateSetItem col : state.getItems()){
set.put(getFieldName2(col.getColumn()), getExpValue(col.getValue()));
}
BSONObject mod = new BasicBSONObject("$set", set);
//coll.updateMulti(query, mod);
coll.update(query, mod, null);
//System.out.println("changs count:"+coll.getStats().size());
return 1;
}
private int DeleteDate(SQLDeleteStatement state) {
SQLTableSource table=state.getTableSource();
DBCollection coll =this._db.getCollection(table.toString());
SQLExpr expr=state.getWhere();
if (expr==null) {
throw new RuntimeException("not where of sql");
}
BSONObject query = parserWhere(expr);
//coll.remove(query);
coll.delete(query);
return 1;
}
private int dropTable(SQLDropTableStatement state) {
for (SQLTableSource table : state.getTableSources()){
//DBCollection coll =this._db.getCollection(table.toString());
//coll.drop();
this._db.dropCollection(table.toString());
}
return 1;
}
private int createTable(SQLCreateTableStatement state) {
//for (SQLTableSource table : state.getTableSource()){
if (!this._db.isCollectionExist(state.getTableSource().toString())) {
this._db.createCollection(state.getTableSource().toString());
}
return 1;
}
private int getSQLExprToInt(SQLExpr expr){
if (expr instanceof SQLIntegerExpr){
return ((SQLIntegerExpr)expr).getNumber().intValue();
}
return 0;
}
private int getSQLExprToAsc(SQLOrderingSpecification ASC){
if (ASC==null ) {
return 1;
}
if (ASC==SQLOrderingSpecification.DESC){
return -1;
}
else {
return 1;
}
}
public String remove(String resource,char ch)
{
StringBuffer buffer=new StringBuffer();
int position=0;
char currentChar;
while(position<resource.length())
{
currentChar=resource.charAt(position++);
if(currentChar!=ch) {
buffer.append(currentChar);
}
}
return buffer.toString();
}
private Object getExpValue(SQLExpr expr){
if (expr instanceof SQLIntegerExpr){
return ((SQLIntegerExpr)expr).getNumber().intValue();
}
if (expr instanceof SQLNumberExpr){
return ((SQLNumberExpr)expr).getNumber().doubleValue();
}
if (expr instanceof SQLCharExpr){
String va=((SQLCharExpr)expr).toString();
return remove(va,'\'');
}
if (expr instanceof SQLBooleanExpr){
return ((SQLBooleanExpr)expr).getValue();
}
if (expr instanceof SQLNullExpr){
return null;
}
if (expr instanceof SQLVariantRefExpr) {
return this._params.get(this._pos++);
}
return expr;
}
private String getExprFieldName(SQLAggregateExpr expr){
String field="";
for (SQLExpr item :expr.getArguments()){
field+=item.toString();
}
return expr.getMethodName()+"("+field+")";
}
private String getFieldName2(SQLExpr item){
return item.toString();
}
private String getFieldName(SQLSelectItem item){
return item.toString();
}
private BSONObject parserWhere(SQLExpr expr){
BasicBSONObject o = new BasicBSONObject();
parserWhere(expr,o);
return o;
}
private void parserDBObject(BasicBSONObject ob,String akey, String aop,Object aval){
boolean isok=false;
if (!(ob.keySet().isEmpty())) {
for (String field : ob.keySet()) {
if (akey.equals(field)){
Object val = ob.get(field);
if (val instanceof BasicBSONObject) {
((BasicBSONObject) val).put(aop, aval);
ob.put(field, (BasicBSONObject) val);
isok=true;
break;
} else if (val instanceof BasicBSONList) {
// newobj.put(field, ((BasicDBList)val).copy());
}
}
}
}
if (isok==false) {
BasicBSONObject xo = new BasicBSONObject();
xo.put(aop, aval);
ob.put(akey,xo);
}
}
@SuppressWarnings("unused")
private void opSQLExpr(SQLBinaryOpExpr expr,BasicBSONObject o) {
SQLExpr exprL=expr.getLeft();
if (!(exprL instanceof SQLBinaryOpExpr))
{
if (expr.getOperator().getName().equals("=")) {
o.put(exprL.toString(), getExpValue(expr.getRight()));
}
else {
//BasicBSONObject xo = new BasicBSONObject();
String op="";
if (expr.getOperator().getName().equals("<")) {
op="$lt";
}
if (expr.getOperator().getName().equals("<=")) {
op = "$lte";
}
if (expr.getOperator().getName().equals(">")) {
op = "$gt";
}
if (expr.getOperator().getName().equals(">=")) {
op = "$gte";
}
if (expr.getOperator().getName().equals("!=")) {
op = "$ne";
}
if (expr.getOperator().getName().equals("<>")) {
op = "$ne";
}
//xo.put(op, getExpValue(expr.getRight()));
// o.put(exprL.toString(),xo);
parserDBObject(o,exprL.toString(),op, getExpValue(expr.getRight()));
}
}
}
private void parserWhere(SQLExpr aexpr,BasicBSONObject o){
if(aexpr instanceof SQLBinaryOpExpr){
SQLBinaryOpExpr expr=(SQLBinaryOpExpr)aexpr;
SQLExpr exprL=expr.getLeft();
if (!(exprL instanceof SQLBinaryOpExpr))
{
//opSQLExpr((SQLBinaryOpExpr)aexpr,o);
if (expr.getOperator().getName().equals("=")) {
o.put(exprL.toString(), getExpValue(expr.getRight()));
}
else {
String op="";
if (expr.getOperator().getName().equals("<")) {
op = "$lt";
}
if (expr.getOperator().getName().equals("<=")) {
op = "$lte";
}
if (expr.getOperator().getName().equals(">")) {
op = "$gt";
}
if (expr.getOperator().getName().equals(">=")) {
op = "$gte";
}
if (expr.getOperator().getName().equals("!=")) {
op = "$ne";
}
if (expr.getOperator().getName().equals("<>")) {
op = "$ne";
}
parserDBObject(o,exprL.toString(),op, getExpValue(expr.getRight()));
}
}
else {
if (expr.getOperator().getName().equals("AND")) {
parserWhere(exprL,o);
parserWhere(expr.getRight(),o);
}
else if (expr.getOperator().getName().equals("OR")) {
orWhere(exprL,expr.getRight(),o);
}
else {
throw new RuntimeException("Can't identify the operation of of where");
}
}
}
}
private void orWhere(SQLExpr exprL,SQLExpr exprR ,BasicBSONObject ob){
BasicBSONObject xo = new BasicBSONObject();
BasicBSONObject yo = new BasicBSONObject();
parserWhere(exprL,xo);
parserWhere(exprR,yo);
ob.put("$or",new Object[]{xo,yo});
}
}
@@ -0,0 +1,329 @@
package io.mycat.backend.jdbc.sequoiadb;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;
/**
* 功能详细描述
* @author sohudo[http://blog.csdn.net/wind520]
* @create 2014年12月19日 下午6:50:23
* @version 0.0.1
*/
public class SequoiaStatement implements Statement
{
private SequoiaConnection _conn;
private final int _type;
private final int _concurrency;
private final int _holdability;
private int _fetchSize = 0;
//int _maxRows = 0;
private SequoiaResultSet _last;
public SequoiaStatement(SequoiaConnection conn, int type, int concurrency, int holdability)
{
this._conn = conn;
this._type = type;
this._concurrency = concurrency;
this._holdability = holdability;
if (this._type != 0) {
throw new UnsupportedOperationException("type not supported yet");
}
if (this._concurrency != 0) {
throw new UnsupportedOperationException("concurrency not supported yet");
}
if (this._holdability != 0) {
throw new UnsupportedOperationException("holdability not supported yet");
}
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
throw new UnsupportedOperationException();//return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
@Override
public ResultSet executeQuery(String sql) throws SQLException {
SequoiaData mongo= new SequoiaSQLParser(this._conn.getDB(), sql).query();
// if (this._fetchSize > 0) {
// //设置每次网络请求的最大记录数
// if (mongo.getCursor()!=null) {
// //mongo.getCursor().batchSize(this._fetchSize);
// }
// }
/*
if (this._maxRows > 0)
{
cursor.limit(this._maxRows);
}
*/
this._last = new SequoiaResultSet(mongo,this._conn.getSchema());
return this._last;
}
@Override
public int executeUpdate(String sql) throws SQLException {
// 执行更新语句
return new SequoiaSQLParser(this._conn.getDB(), sql).executeUpdate();
}
@Override
public void close() throws SQLException {
this._conn = null;
}
@Override
public int getMaxFieldSize() throws SQLException {
// 获取可以为此 Statement 对象所生成 ResultSet 对象中的字符和二进制列值返回的最大字节数。
return 0;//this._fetchSize;
}
@Override
public void setMaxFieldSize(int max) throws SQLException {
//this._fetchSize=max;
}
@Override
public int getMaxRows() throws SQLException {
// 获取由此 Statement 对象生成的 ResultSet 对象可以包含的最大行数。
return 0;//this._maxRows;
}
@Override
public void setMaxRows(int max) throws SQLException {
//this._maxRows = max;
}
@Override
public void setEscapeProcessing(boolean enable) throws SQLException {
// 将转义处理设置为开或关。
}
@Override
public int getQueryTimeout() throws SQLException {
return 0;
}
@Override
public void setQueryTimeout(int seconds) throws SQLException {
// Statement 对象执行的秒数设置,超时设置。
}
@Override
public void cancel() throws SQLException {
}
@Override
public SQLWarning getWarnings() throws SQLException {
return null;
}
@Override
public void clearWarnings() throws SQLException {
}
@Override
public void setCursorName(String name) throws SQLException {
// 将 SQL 指针名称设置为给定的 String,后续 Statement 对象的 execute 方法将使用此字符串。
}
@Override
public boolean execute(String sql) throws SQLException {
int i=0;//new SequoiaSQLParser(this._conn.getDB(), sql).executeUpdate();
return i>0;
}
@Override
public ResultSet getResultSet() throws SQLException {
return this._last;
}
@Override
public int getUpdateCount() throws SQLException {
// 记录变更的数量,ResultSet 对象或没有更多结果,则返回 -1
return 0;
}
@Override
public boolean getMoreResults() throws SQLException {
// 移动到此 Statement 对象的下一个结果,如果其为 ResultSet 对象,则返回 true,并隐式关闭利用方法 getResultSet 获取的所有当前 ResultSet 对象。
return false;
}
@Override
public void setFetchDirection(int direction) throws SQLException {
// 此 Statement 对象创建的 ResultSet 对象中将按该方向处理行。
}
@Override
public int getFetchDirection() throws SQLException {
return 0;
}
@Override
public void setFetchSize(int rows) throws SQLException {
// 获取结果集合的行数,该数是根据此 Statement 对象生成的 ResultSet 对象的默认获取大小。
this._fetchSize=rows;
}
@Override
public int getFetchSize() throws SQLException {
// 获取结果集合的行数,该数是根据此 Statement 对象生成的 ResultSet 对象的默认获取大小。
return this._fetchSize;
}
@Override
public int getResultSetConcurrency() throws SQLException {
// 对象生成的 ResultSet 对象的结果集合并发性
return 0;
}
@Override
public int getResultSetType() throws SQLException {
// 对象生成的 ResultSet 对象的结果集合类型。
return 0;
}
@Override
public void addBatch(String sql) throws SQLException {
// 新增批处理
throw new UnsupportedOperationException("batch not supported");
}
@Override
public void clearBatch() throws SQLException {
}
@Override
public int[] executeBatch() throws SQLException {
// 将一批命令提交给数据库来执行,如果全部命令执行成功,则返回更新计数组成的数组。
return null;
}
@Override
public Connection getConnection() throws SQLException {
return this._conn;
}
@Override
public boolean getMoreResults(int current) throws SQLException {
// 将此 Statement 对象移动到下一个结果,根据给定标志指定的指令处理所有当前 ResultSet 对象;如果下一个结果为 ResultSet 对象,则返回 true。
return false;
}
@Override
public ResultSet getGeneratedKeys() throws SQLException {
// 获取由于执行此 Statement 对象而创建的所有自动生成的键。
return null;
}
@Override
public int executeUpdate(String sql, int autoGeneratedKeys)
throws SQLException {
return 0;
//throw new RuntimeException("executeUpdate not done");
}
@Override
public int executeUpdate(String sql, int[] columnIndexes)
throws SQLException {
return 0;
//throw new RuntimeException("executeUpdate not done");
}
@Override
public int executeUpdate(String sql, String[] columnNames)
throws SQLException {
return 0;
//throw new RuntimeException("executeUpdate not done");
}
@Override
public boolean execute(String sql, int autoGeneratedKeys)
throws SQLException {
return false;
}
@Override
public boolean execute(String sql, int[] columnIndexes) throws SQLException {
return false;
}
@Override
public boolean execute(String sql, String[] columnNames)
throws SQLException {
return false;
}
@Override
public int getResultSetHoldability() throws SQLException {
return 0;
}
@Override
public boolean isClosed() throws SQLException {
return this._conn == null;
}
@Override
public void setPoolable(boolean poolable) throws SQLException {
// 请求将 Statement 池化或非池化
}
@Override
public boolean isPoolable() throws SQLException {
return false;
}
@Override
public void closeOnCompletion() throws SQLException {
}
@Override
public boolean isCloseOnCompletion() throws SQLException {
return false;
}
}
@@ -0,0 +1,16 @@
package io.mycat.backend.jdbc.sequoiadb;
public class StringUtils {
public static boolean startsWithIgnoreCase(String searchIn, int startAt,
String searchFor) {
return searchIn.regionMatches(true, startAt, searchFor, 0, searchFor
.length());
}
public static boolean startsWithIgnoreCase(String searchIn, String searchFor) {
return startsWithIgnoreCase(searchIn, 0, searchFor);
}
}
@@ -0,0 +1,66 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat.backend.mysql;
/**
* @author mycat
*/
public class BindValue {
public boolean isNull; /* NULL indicator */
public boolean isLongData; /* long data indicator */
public boolean isSet; /* has this parameter been set */
public long length; /* Default length of data */
public int type; /* data type */
public byte scale;
/** 数据值 **/
public byte byteBinding;
public short shortBinding;
public int intBinding;
public float floatBinding;
public long longBinding;
public double doubleBinding;
public Object value; /* Other value to store */
public void reset() {
this.isNull = false;
this.isLongData = false;
this.isSet = false;
this.length = 0;
this.type = 0;
this.scale = 0;
this.byteBinding = 0;
this.shortBinding = 0;
this.intBinding = 0;
this.floatBinding = 0;
this.longBinding = 0L;
this.doubleBinding = 0D;
this.value = null;
}
}
@@ -0,0 +1,90 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat.backend.mysql;
import java.io.UnsupportedEncodingException;
import io.mycat.config.Fields;
/**
* @author mycat
*/
public class BindValueUtil {
public static final void read(MySQLMessage mm, BindValue bv, String charset) throws UnsupportedEncodingException {
switch (bv.type & 0xff) {
case Fields.FIELD_TYPE_BIT:
bv.value = mm.readBytesWithLength();
break;
case Fields.FIELD_TYPE_TINY:
bv.byteBinding = mm.read();
break;
case Fields.FIELD_TYPE_SHORT:
bv.shortBinding = (short) mm.readUB2();
break;
case Fields.FIELD_TYPE_LONG:
bv.intBinding = mm.readInt();
break;
case Fields.FIELD_TYPE_LONGLONG:
bv.longBinding = mm.readLong();
break;
case Fields.FIELD_TYPE_FLOAT:
bv.floatBinding = mm.readFloat();
break;
case Fields.FIELD_TYPE_DOUBLE:
bv.doubleBinding = mm.readDouble();
break;
case Fields.FIELD_TYPE_TIME:
bv.value = mm.readTime();
break;
case Fields.FIELD_TYPE_DATE:
case Fields.FIELD_TYPE_DATETIME:
case Fields.FIELD_TYPE_TIMESTAMP:
bv.value = mm.readDate();
break;
case Fields.FIELD_TYPE_VAR_STRING:
case Fields.FIELD_TYPE_STRING:
case Fields.FIELD_TYPE_VARCHAR:
bv.value = mm.readStringWithLength(charset);
if (bv.value == null) {
bv.isNull = true;
}
break;
case Fields.FIELD_TYPE_DECIMAL:
case Fields.FIELD_TYPE_NEW_DECIMAL:
bv.value = mm.readBigDecimal();
if (bv.value == null) {
bv.isNull = true;
}
break;
case Fields.FIELD_TYPE_BLOB:
bv.isLongData = true;
break;
default:
throw new IllegalArgumentException("bindValue error,unsupported type:" + bv.type);
}
bv.isSet = true;
}
}
@@ -0,0 +1,147 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat.backend.mysql;
import java.nio.ByteBuffer;
/**
* @author mycat
*/
public class BufferUtil {
public static final void writeUB2(ByteBuffer buffer, int i) {
buffer.put((byte) (i & 0xff));
buffer.put((byte) (i >>> 8));
}
public static final void writeUB3(ByteBuffer buffer, int i) {
buffer.put((byte) (i & 0xff));
buffer.put((byte) (i >>> 8));
buffer.put((byte) (i >>> 16));
}
public static final void writeInt(ByteBuffer buffer, int i) {
buffer.put((byte) (i & 0xff));
buffer.put((byte) (i >>> 8));
buffer.put((byte) (i >>> 16));
buffer.put((byte) (i >>> 24));
}
public static final void writeFloat(ByteBuffer buffer, float f) {
writeInt(buffer, Float.floatToIntBits(f));
}
public static final void writeUB4(ByteBuffer buffer, long l) {
buffer.put((byte) (l & 0xff));
buffer.put((byte) (l >>> 8));
buffer.put((byte) (l >>> 16));
buffer.put((byte) (l >>> 24));
}
public static final void writeLong(ByteBuffer buffer, long l) {
buffer.put((byte) (l & 0xff));
buffer.put((byte) (l >>> 8));
buffer.put((byte) (l >>> 16));
buffer.put((byte) (l >>> 24));
buffer.put((byte) (l >>> 32));
buffer.put((byte) (l >>> 40));
buffer.put((byte) (l >>> 48));
buffer.put((byte) (l >>> 56));
}
public static final void writeDouble(ByteBuffer buffer, double d) {
writeLong(buffer, Double.doubleToLongBits(d));
}
public static final void writeLength(ByteBuffer buffer, long l) {
if (l < 251) {
buffer.put((byte) l);
} else if (l < 0x10000L) {
buffer.put((byte) 252);
writeUB2(buffer, (int) l);
} else if (l < 0x1000000L) {
buffer.put((byte) 253);
writeUB3(buffer, (int) l);
} else {
buffer.put((byte) 254);
writeLong(buffer, l);
}
}
public static final void writeWithNull(ByteBuffer buffer, byte[] src) {
buffer.put(src);
buffer.put((byte) 0);
}
public static final void writeWithLength(ByteBuffer buffer, byte[] src) {
int length = src.length;
if (length < 251) {
buffer.put((byte) length);
} else if (length < 0x10000L) {
buffer.put((byte) 252);
writeUB2(buffer, length);
} else if (length < 0x1000000L) {
buffer.put((byte) 253);
writeUB3(buffer, length);
} else {
buffer.put((byte) 254);
writeLong(buffer, length);
}
buffer.put(src);
}
public static final void writeWithLength(ByteBuffer buffer, byte[] src, byte nullValue) {
if (src == null) {
buffer.put(nullValue);
} else {
writeWithLength(buffer, src);
}
}
public static final int getLength(long length) {
if (length < 251) {
return 1;
} else if (length < 0x10000L) {
return 3;
} else if (length < 0x1000000L) {
return 4;
} else {
return 9;
}
}
public static final int getLength(byte[] src) {
int length = src.length;
if (length < 251) {
return 1 + length;
} else if (length < 0x10000L) {
return 3 + length;
} else if (length < 0x1000000L) {
return 4 + length;
} else {
return 9 + length;
}
}
}
@@ -0,0 +1,116 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat.backend.mysql;
/**
* @author mycat
*/
public class ByteUtil {
public static int readUB2(byte[] data, int offset) {
int i = data[offset] & 0xff;
i |= (data[++offset] & 0xff) << 8;
return i;
}
public static int readUB3(byte[] data, int offset) {
int i = data[offset] & 0xff;
i |= (data[++offset] & 0xff) << 8;
i |= (data[++offset] & 0xff) << 16;
return i;
}
public static long readUB4(byte[] data, int offset) {
long l = data[offset] & 0xff;
l |= (data[++offset] & 0xff) << 8;
l |= (data[++offset] & 0xff) << 16;
l |= (data[++offset] & 0xff) << 24;
return l;
}
public static long readLong(byte[] data, int offset) {
long l = (long) (data[offset] & 0xff);
l |= (long) (data[++offset] & 0xff) << 8;
l |= (long) (data[++offset] & 0xff) << 16;
l |= (long) (data[++offset] & 0xff) << 24;
l |= (long) (data[++offset] & 0xff) << 32;
l |= (long) (data[++offset] & 0xff) << 40;
l |= (long) (data[++offset] & 0xff) << 48;
l |= (long) (data[++offset] & 0xff) << 56;
return l;
}
public static long readLength(byte[] data, int offset) {
int length = data[offset++] & 0xff;
switch (length) {
case 251:
return MySQLMessage.NULL_LENGTH;
case 252:
return readUB2(data, offset);
case 253:
return readUB3(data, offset);
case 254:
return readLong(data, offset);
default:
return length;
}
}
public static int lengthToZero(byte[] data, int offset) {
int start = offset;
for (int i = start; i < data.length; i++) {
if (data[i] == 0) {
return (i - start);
}
}
int remaining = data.length - start;
return remaining > 0 ? remaining : 0;
}
public static int decodeLength(byte[] src) {
int length = src.length;
if (length < 251) {
return 1 + length;
} else if (length < 0x10000L) {
return 3 + length;
} else if (length < 0x1000000L) {
return 4 + length;
} else {
return 9 + length;
}
}
public static int decodeLength(long length) {
if (length < 251) {
return 1;
} else if (length < 0x10000L) {
return 3;
} else if (length < 0x1000000L) {
return 4;
} else {
return 9;
}
}
}
@@ -0,0 +1,93 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat.backend.mysql;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.FileInputStream;
import java.util.*;
/**
* @author mycat
*/
public class CharsetUtil {
public static final Logger logger = LoggerFactory
.getLogger(CharsetUtil.class);
private static final Map<Integer,String> INDEX_TO_CHARSET = new HashMap<>();
private static final Map<String, Integer> CHARSET_TO_INDEX = new HashMap<>();
static {
// index_to_charset.properties
INDEX_TO_CHARSET.put(1,"big5");
INDEX_TO_CHARSET.put(8,"latin1");
INDEX_TO_CHARSET.put(9,"latin2");
INDEX_TO_CHARSET.put(14,"cp1251");
INDEX_TO_CHARSET.put(28,"gbk");
INDEX_TO_CHARSET.put(24,"gb2312");
INDEX_TO_CHARSET.put(33,"utf8");
INDEX_TO_CHARSET.put(45,"utf8mb4");
String filePath = Thread.currentThread().getContextClassLoader()
.getResource("").getPath().replaceAll("%20", " ")
+ "index_to_charset.properties";
Properties prop = new Properties();
try {
prop.load(new FileInputStream(filePath));
for (Object index : prop.keySet()){
INDEX_TO_CHARSET.put(Integer.parseInt((String) index), prop.getProperty((String) index));
}
} catch (Exception e) {
logger.error("error:",e);
}
// charset --> index
for(Integer key : INDEX_TO_CHARSET.keySet()){
String charset = INDEX_TO_CHARSET.get(key);
if(charset != null && CHARSET_TO_INDEX.get(charset) == null){
CHARSET_TO_INDEX.put(charset, key);
}
}
CHARSET_TO_INDEX.put("iso-8859-1", 14);
CHARSET_TO_INDEX.put("iso_8859_1", 14);
CHARSET_TO_INDEX.put("utf-8", 33);
}
public static final String getCharset(int index) {
return INDEX_TO_CHARSET.get(index);
}
public static final int getIndex(String charset) {
if (charset == null || charset.length() == 0) {
return 0;
} else {
Integer i = CHARSET_TO_INDEX.get(charset.toLowerCase());
return (i == null) ? 0 : i;
}
}
}
@@ -0,0 +1,106 @@
package io.mycat.backend.mysql;
import java.io.*;
import java.util.List;
import io.mycat.MycatServer;
import io.mycat.backend.BackendConnection;
import io.mycat.net.BackendAIOConnection;
import io.mycat.net.mysql.BinaryPacket;
import io.mycat.net.mysql.CommandPacket;
import io.mycat.net.mysql.MySQLPacket;
import io.mycat.route.RouteResultsetNode;
import io.mycat.sqlengine.mpp.LoadData;
/**
* Created by nange on 2015/3/31.
*/
public class LoadDataUtil
{
public static void requestFileDataResponse(byte[] data, BackendConnection conn)
{
byte packId= data[3];
BackendAIOConnection backendAIOConnection= (BackendAIOConnection) conn;
RouteResultsetNode rrn= (RouteResultsetNode) conn.getAttachment();
LoadData loadData= rrn.getLoadData();
List<String> loadDataData = loadData.getData();
try
{
if(loadDataData !=null&&loadDataData.size()>0)
{
ByteArrayOutputStream bos = new ByteArrayOutputStream();
for (int i = 0, loadDataDataSize = loadDataData.size(); i < loadDataDataSize; i++)
{
String line = loadDataData.get(i);
String s =(i==loadDataDataSize-1)?line: line + loadData.getLineTerminatedBy();
byte[] bytes = s.getBytes(loadData.getCharset());
bos.write(bytes);
}
packId= writeToBackConnection(packId,new ByteArrayInputStream(bos.toByteArray()),backendAIOConnection);
} else
{
//从文件读取
packId= writeToBackConnection(packId,new BufferedInputStream(new FileInputStream(loadData.getFileName())),backendAIOConnection);
}
}catch (IOException e)
{
throw new RuntimeException(e);
} finally
{
//结束必须发空包
byte[] empty = new byte[] { 0, 0, 0,3 };
empty[3]=++packId;
backendAIOConnection.write(empty);
}
}
public static byte writeToBackConnection(byte packID,InputStream inputStream,BackendAIOConnection backendAIOConnection) throws IOException
{
try
{
int packSize = MycatServer.getInstance().getConfig().getSystem().getBufferPoolChunkSize() - 5;
// int packSize = backendAIOConnection.getMaxPacketSize() / 32;
// int packSize=65530;
byte[] buffer = new byte[packSize];
int len = -1;
while ((len = inputStream.read(buffer)) != -1)
{
byte[] temp = null;
if (len == packSize)
{
temp = buffer;
} else
{
temp = new byte[len];
System.arraycopy(buffer, 0, temp, 0, len);
}
BinaryPacket packet = new BinaryPacket();
packet.packetId = ++packID;
packet.data = temp;
packet.write(backendAIOConnection);
}
}
finally
{
inputStream.close();
}
return packID;
}
}
@@ -0,0 +1,361 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat.backend.mysql;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.Arrays;
import java.util.Calendar;
/**
* @author mycat
*/
public class MySQLMessage {
public static final long NULL_LENGTH = -1;
private static final byte[] EMPTY_BYTES = new byte[0];
private final byte[] data;
private final int length;
private int position;
public MySQLMessage(byte[] data) {
this.data = data;
this.length = data.length;
this.position = 0;
}
public int length() {
return length;
}
public int position() {
return position;
}
public byte[] bytes() {
return data;
}
public void move(int i) {
position += i;
}
public void position(int i) {
this.position = i;
}
public boolean hasRemaining() {
return length > position;
}
public byte read(int i) {
return data[i];
}
public byte read() {
return data[position++];
}
public int readUB2() {
final byte[] b = this.data;
int i = b[position++] & 0xff;
i |= (b[position++] & 0xff) << 8;
return i;
}
public int readUB3() {
final byte[] b = this.data;
int i = b[position++] & 0xff;
i |= (b[position++] & 0xff) << 8;
i |= (b[position++] & 0xff) << 16;
return i;
}
public long readUB4() {
final byte[] b = this.data;
long l = (long) (b[position++] & 0xff);
l |= (long) (b[position++] & 0xff) << 8;
l |= (long) (b[position++] & 0xff) << 16;
l |= (long) (b[position++] & 0xff) << 24;
return l;
}
public int readInt() {
final byte[] b = this.data;
int i = b[position++] & 0xff;
i |= (b[position++] & 0xff) << 8;
i |= (b[position++] & 0xff) << 16;
i |= (b[position++] & 0xff) << 24;
return i;
}
public float readFloat() {
return Float.intBitsToFloat(readInt());
}
public long readLong() {
final byte[] b = this.data;
long l = (long) (b[position++] & 0xff);
l |= (long) (b[position++] & 0xff) << 8;
l |= (long) (b[position++] & 0xff) << 16;
l |= (long) (b[position++] & 0xff) << 24;
l |= (long) (b[position++] & 0xff) << 32;
l |= (long) (b[position++] & 0xff) << 40;
l |= (long) (b[position++] & 0xff) << 48;
l |= (long) (b[position++] & 0xff) << 56;
return l;
}
public double readDouble() {
return Double.longBitsToDouble(readLong());
}
public long readLength() {
int length = data[position++] & 0xff;
switch (length) {
case 251:
return NULL_LENGTH;
case 252:
return readUB2();
case 253:
return readUB3();
case 254:
return readLong();
default:
return length;
}
}
public byte[] readBytes() {
if (position >= length) {
return EMPTY_BYTES;
}
byte[] ab = new byte[length - position];
System.arraycopy(data, position, ab, 0, ab.length);
position = length;
return ab;
}
public byte[] readBytes(int length) {
byte[] ab = new byte[length];
System.arraycopy(data, position, ab, 0, length);
position += length;
return ab;
}
public byte[] readBytesWithNull() {
final byte[] b = this.data;
if (position >= length) {
return EMPTY_BYTES;
}
int offset = -1;
for (int i = position; i < length; i++) {
if (b[i] == 0) {
offset = i;
break;
}
}
switch (offset) {
case -1:
byte[] ab1 = new byte[length - position];
System.arraycopy(b, position, ab1, 0, ab1.length);
position = length;
return ab1;
case 0:
position++;
return EMPTY_BYTES;
default:
byte[] ab2 = new byte[offset - position];
System.arraycopy(b, position, ab2, 0, ab2.length);
position = offset + 1;
return ab2;
}
}
public byte[] readBytesWithLength() {
int length = (int) readLength();
if(length==NULL_LENGTH)
{
return null;
}
if (length <= 0) {
return EMPTY_BYTES;
}
byte[] ab = new byte[length];
System.arraycopy(data, position, ab, 0, ab.length);
position += length;
return ab;
}
public String readString() {
if (position >= length) {
return null;
}
String s = new String(data, position, length - position);
position = length;
return s;
}
public String readString(String charset) throws UnsupportedEncodingException {
if (position >= length) {
return null;
}
String s = new String(data, position, length - position, charset);
position = length;
return s;
}
public String readStringWithNull() {
final byte[] b = this.data;
if (position >= length) {
return null;
}
int offset = -1;
for (int i = position; i < length; i++) {
if (b[i] == 0) {
offset = i;
break;
}
}
if (offset == -1) {
String s = new String(b, position, length - position);
position = length;
return s;
}
if (offset > position) {
String s = new String(b, position, offset - position);
position = offset + 1;
return s;
} else {
position++;
return null;
}
}
public String readStringWithNull(String charset) throws UnsupportedEncodingException {
final byte[] b = this.data;
if (position >= length) {
return null;
}
int offset = -1;
for (int i = position; i < length; i++) {
if (b[i] == 0) {
offset = i;
break;
}
}
switch (offset) {
case -1:
String s1 = new String(b, position, length - position, charset);
position = length;
return s1;
case 0:
position++;
return null;
default:
String s2 = new String(b, position, offset - position, charset);
position = offset + 1;
return s2;
}
}
public String readStringWithLength() {
int length = (int) readLength();
if (length <= 0) {
return null;
}
String s = new String(data, position, length);
position += length;
return s;
}
public String readStringWithLength(String charset) throws UnsupportedEncodingException {
int length = (int) readLength();
if (length <= 0) {
return null;
}
String s = new String(data, position, length, charset);
position += length;
return s;
}
public java.sql.Time readTime() {
move(6);
int hour = read();
int minute = read();
int second = read();
Calendar cal = getLocalCalendar();
cal.set(0, 0, 0, hour, minute, second);
return new Time(cal.getTimeInMillis());
}
public java.util.Date readDate() {
byte length = read();
int year = readUB2();
byte month = read();
byte date = read();
int hour = read();
int minute = read();
int second = read();
if (length == 11) {
long nanos = readUB4();
Calendar cal = getLocalCalendar();
cal.set(year, --month, date, hour, minute, second);
Timestamp time = new Timestamp(cal.getTimeInMillis());
time.setNanos((int) nanos);
return time;
} else {
Calendar cal = getLocalCalendar();
cal.set(year, --month, date, hour, minute, second);
return new java.sql.Date(cal.getTimeInMillis());
}
}
public BigDecimal readBigDecimal() {
String src = readStringWithLength();
return src == null ? null : new BigDecimal(src);
}
public String toString() {
return new StringBuilder().append(Arrays.toString(data)).toString();
}
private static final ThreadLocal<Calendar> localCalendar = new ThreadLocal<Calendar>();
private static final Calendar getLocalCalendar() {
Calendar cal = localCalendar.get();
if (cal == null) {
cal = Calendar.getInstance();
localCalendar.set(cal);
}
return cal;
}
}
@@ -0,0 +1,91 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat.backend.mysql;
import java.io.UnsupportedEncodingException;
import io.mycat.config.ErrorCode;
import io.mycat.net.mysql.BinaryPacket;
import io.mycat.net.mysql.ErrorPacket;
import io.mycat.net.mysql.FieldPacket;
import io.mycat.net.mysql.ResultSetHeaderPacket;
/**
* @author mycat
*/
public class PacketUtil {
private static final String CODE_PAGE_1252 = "Cp1252";
public static final ResultSetHeaderPacket getHeader(int fieldCount) {
ResultSetHeaderPacket packet = new ResultSetHeaderPacket();
packet.packetId = 1;
packet.fieldCount = fieldCount;
return packet;
}
public static byte[] encode(String src, String charset) {
if (src == null) {
return null;
}
try {
return src.getBytes(charset);
} catch (UnsupportedEncodingException e) {
return src.getBytes();
}
}
public static final FieldPacket getField(String name, String orgName, int type) {
FieldPacket packet = new FieldPacket();
packet.charsetIndex = CharsetUtil.getIndex(CODE_PAGE_1252);
packet.name = encode(name, CODE_PAGE_1252);
packet.orgName = encode(orgName, CODE_PAGE_1252);
packet.type = (byte) type;
return packet;
}
public static final FieldPacket getField(String name, int type) {
FieldPacket packet = new FieldPacket();
packet.charsetIndex = CharsetUtil.getIndex(CODE_PAGE_1252);
packet.name = encode(name, CODE_PAGE_1252);
packet.type = (byte) type;
return packet;
}
public static final ErrorPacket getShutdown() {
ErrorPacket error = new ErrorPacket();
error.packetId = 1;
error.errno = ErrorCode.ER_SERVER_SHUTDOWN;
error.message = "The server has been shutdown".getBytes();
return error;
}
public static final FieldPacket getField(BinaryPacket src, String fieldName) {
FieldPacket field = new FieldPacket();
field.read(src);
field.name = encode(fieldName, CODE_PAGE_1252);
field.packetLength = field.calcPacketSize();
return field;
}
}
@@ -0,0 +1,107 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat.backend.mysql;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* @author mycat, CrazyPig
*/
public class PreparedStatement {
private long id;
private String statement;
private int columnsNumber;
private int parametersNumber;
private int[] parametersType;
/**
* 存放COM_STMT_SEND_LONG_DATA命令发送过来的字节数据
* <pre>
* key : param_id
* value : byte data
* </pre>
*/
private Map<Long, ByteArrayOutputStream> longDataMap;
public PreparedStatement(long id, String statement, int columnsNumber, int parametersNumber) {
this.id = id;
this.statement = statement;
this.columnsNumber = columnsNumber;
this.parametersNumber = parametersNumber;
this.parametersType = new int[parametersNumber];
this.longDataMap = new HashMap<Long, ByteArrayOutputStream>();
}
public long getId() {
return id;
}
public String getStatement() {
return statement;
}
public int getColumnsNumber() {
return columnsNumber;
}
public int getParametersNumber() {
return parametersNumber;
}
public int[] getParametersType() {
return parametersType;
}
public ByteArrayOutputStream getLongData(long paramId) {
return longDataMap.get(paramId);
}
/**
* COM_STMT_RESET命令将调用该方法进行数据重置
*/
public void resetLongData() {
for(Long paramId : longDataMap.keySet()) {
longDataMap.get(paramId).reset();
}
}
/**
* 追加数据到指定的预处理参数
* @param paramId
* @param data
* @throws IOException
*/
public void appendLongData(long paramId, byte[] data) throws IOException {
if(getLongData(paramId) == null) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(data);
longDataMap.put(paramId, out);
} else {
longDataMap.get(paramId).write(data);
}
}
}
@@ -0,0 +1,102 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat.backend.mysql;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* 加密解密工具类
*
* @author mycat
*/
public class SecurityUtil {
public static final byte[] scramble411(byte[] pass, byte[] seed) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-1");
byte[] pass1 = md.digest(pass);
md.reset();
byte[] pass2 = md.digest(pass1);
md.reset();
md.update(seed);
byte[] pass3 = md.digest(pass2);
for (int i = 0; i < pass3.length; i++) {
pass3[i] = (byte) (pass3[i] ^ pass1[i]);
}
return pass3;
}
public static final String scramble323(String pass, String seed) {
if ((pass == null) || (pass.length() == 0)) {
return pass;
}
byte b;
double d;
long[] pw = hash(seed);
long[] msg = hash(pass);
long max = 0x3fffffffL;
long seed1 = (pw[0] ^ msg[0]) % max;
long seed2 = (pw[1] ^ msg[1]) % max;
char[] chars = new char[seed.length()];
for (int i = 0; i < seed.length(); i++) {
seed1 = ((seed1 * 3) + seed2) % max;
seed2 = (seed1 + seed2 + 33) % max;
d = (double) seed1 / (double) max;
b = (byte) java.lang.Math.floor((d * 31) + 64);
chars[i] = (char) b;
}
seed1 = ((seed1 * 3) + seed2) % max;
// seed2 = (seed1 + seed2 + 33) % max;
d = (double) seed1 / (double) max;
b = (byte) java.lang.Math.floor(d * 31);
for (int i = 0; i < seed.length(); i++) {
chars[i] ^= (char) b;
}
return new String(chars);
}
private static long[] hash(String src) {
long nr = 1345345333L;
long add = 7;
long nr2 = 0x12345671L;
long tmp;
for (int i = 0; i < src.length(); ++i) {
switch (src.charAt(i)) {
case ' ':
case '\t':
continue;
default:
tmp = (0xff & src.charAt(i));
nr ^= ((((nr & 63) + add) * tmp) + (nr << 8));
nr2 += ((nr2 << 8) ^ nr);
add += tmp;
}
}
long[] result = new long[2];
result[0] = nr & 0x7fffffffL;
result[1] = nr2 & 0x7fffffffL;
return result;
}
}
@@ -0,0 +1,240 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat.backend.mysql;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* @author mycat
*/
public class StreamUtil {
private static final long NULL_LENGTH = -1;
private static final byte[] EMPTY_BYTES = new byte[0];
public static final void read(InputStream in, byte[] b, int offset, int length) throws IOException {
for (int got = 0; length > 0;) {
got = in.read(b, offset, length);
if (got < 0) {
throw new EOFException();
}
offset += got;
length -= got;
}
}
public static final byte read(InputStream in) throws IOException {
int got = in.read();
if (got < 0) {
throw new EOFException();
}
return (byte) (got & 0xff);
}
public static final int readUB2(InputStream in) throws IOException {
byte[] b = new byte[2];
read(in, b, 0, b.length);
int i = b[0] & 0xff;
i |= (b[1] & 0xff) << 8;
return i;
}
public static final int readUB3(InputStream in) throws IOException {
byte[] b = new byte[3];
read(in, b, 0, b.length);
int i = b[0] & 0xff;
i |= (b[1] & 0xff) << 8;
i |= (b[2] & 0xff) << 16;
return i;
}
public static final int readInt(InputStream in) throws IOException {
byte[] b = new byte[4];
read(in, b, 0, b.length);
int i = b[0] & 0xff;
i |= (b[1] & 0xff) << 8;
i |= (b[2] & 0xff) << 16;
i |= (b[3] & 0xff) << 24;
return i;
}
public static final float readFloat(InputStream in) throws IOException {
return Float.intBitsToFloat(readInt(in));
}
public static final long readUB4(InputStream in) throws IOException {
byte[] b = new byte[4];
read(in, b, 0, b.length);
long l = (long) (b[0] & 0xff);
l |= (long) (b[1] & 0xff) << 8;
l |= (long) (b[2] & 0xff) << 16;
l |= (long) (b[3] & 0xff) << 24;
return l;
}
public static final long readLong(InputStream in) throws IOException {
byte[] b = new byte[8];
read(in, b, 0, b.length);
long l = (long) (b[0] & 0xff);
l |= (long) (b[1] & 0xff) << 8;
l |= (long) (b[2] & 0xff) << 16;
l |= (long) (b[3] & 0xff) << 24;
l |= (long) (b[4] & 0xff) << 32;
l |= (long) (b[5] & 0xff) << 40;
l |= (long) (b[6] & 0xff) << 48;
l |= (long) (b[7] & 0xff) << 56;
return l;
}
public static final double readDouble(InputStream in) throws IOException {
return Double.longBitsToDouble(readLong(in));
}
public static final byte[] readWithLength(InputStream in) throws IOException {
int length = (int) readLength(in);
if (length <= 0) {
return EMPTY_BYTES;
}
byte[] b = new byte[length];
read(in, b, 0, b.length);
return b;
}
public static final void write(OutputStream out, byte b) throws IOException {
out.write(b & 0xff);
}
public static final void writeUB2(OutputStream out, int i) throws IOException {
byte[] b = new byte[2];
b[0] = (byte) (i & 0xff);
b[1] = (byte) (i >>> 8);
out.write(b);
}
public static final void writeUB3(OutputStream out, int i) throws IOException {
byte[] b = new byte[3];
b[0] = (byte) (i & 0xff);
b[1] = (byte) (i >>> 8);
b[2] = (byte) (i >>> 16);
out.write(b);
}
public static final void writeInt(OutputStream out, int i) throws IOException {
byte[] b = new byte[4];
b[0] = (byte) (i & 0xff);
b[1] = (byte) (i >>> 8);
b[2] = (byte) (i >>> 16);
b[3] = (byte) (i >>> 24);
out.write(b);
}
public static final void writeFloat(OutputStream out, float f) throws IOException {
writeInt(out, Float.floatToIntBits(f));
}
public static final void writeUB4(OutputStream out, long l) throws IOException {
byte[] b = new byte[4];
b[0] = (byte) (l & 0xff);
b[1] = (byte) (l >>> 8);
b[2] = (byte) (l >>> 16);
b[3] = (byte) (l >>> 24);
out.write(b);
}
public static final void writeLong(OutputStream out, long l) throws IOException {
byte[] b = new byte[8];
b[0] = (byte) (l & 0xff);
b[1] = (byte) (l >>> 8);
b[2] = (byte) (l >>> 16);
b[3] = (byte) (l >>> 24);
b[4] = (byte) (l >>> 32);
b[5] = (byte) (l >>> 40);
b[6] = (byte) (l >>> 48);
b[7] = (byte) (l >>> 56);
out.write(b);
}
public static final void writeDouble(OutputStream out, double d) throws IOException {
writeLong(out, Double.doubleToLongBits(d));
}
public static final long readLength(InputStream in) throws IOException {
int length = in.read();
if (length < 0) {
throw new EOFException();
}
switch (length) {
case 251:
return NULL_LENGTH;
case 252:
return readUB2(in);
case 253:
return readUB3(in);
case 254:
return readLong(in);
default:
return length;
}
}
public static final void writeLength(OutputStream out, long length) throws IOException {
if (length < 251) {
out.write((byte) length);
} else if (length < 0x10000L) {
out.write((byte) 252);
writeUB2(out, (int) length);
} else if (length < 0x1000000L) {
out.write((byte) 253);
writeUB3(out, (int) length);
} else {
out.write((byte) 254);
writeLong(out, length);
}
}
public static final void writeWithNull(OutputStream out, byte[] src) throws IOException {
out.write(src);
out.write((byte) 0);
}
public static final void writeWithLength(OutputStream out, byte[] src) throws IOException {
int length = src.length;
if (length < 251) {
out.write((byte) length);
} else if (length < 0x10000L) {
out.write((byte) 252);
writeUB2(out, length);
} else if (length < 0x1000000L) {
out.write((byte) 253);
writeUB3(out, length);
} else {
out.write((byte) 254);
writeLong(out, length);
}
out.write(src);
}
}
@@ -0,0 +1,668 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat.backend.mysql.nio;
import io.mycat.backend.mysql.xa.TxState;
import org.slf4j.Logger; import org.slf4j.LoggerFactory;
import io.mycat.MycatServer;
import io.mycat.backend.mysql.CharsetUtil;
import io.mycat.backend.mysql.SecurityUtil;
import io.mycat.backend.mysql.nio.handler.ResponseHandler;
import io.mycat.config.Capabilities;
import io.mycat.config.Isolations;
import io.mycat.net.BackendAIOConnection;
import io.mycat.net.mysql.*;
import io.mycat.route.RouteResultsetNode;
import io.mycat.server.ServerConnection;
import io.mycat.server.parser.ServerParse;
import io.mycat.util.TimeUtil;
import io.mycat.util.exception.UnknownTxIsolationException;
import java.io.UnsupportedEncodingException;
import java.nio.channels.NetworkChannel;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author mycat
*/
public class MySQLConnection extends BackendAIOConnection {
private static final Logger LOGGER = LoggerFactory
.getLogger(MySQLConnection.class);
private static final long CLIENT_FLAGS = initClientFlags();
private volatile long lastTime;
private volatile String schema = null;
private volatile String oldSchema;
private volatile boolean borrowed = false;
private volatile boolean modifiedSQLExecuted = false;
private volatile int batchCmdCount = 0;
private static long initClientFlags() {
int flag = 0;
flag |= Capabilities.CLIENT_LONG_PASSWORD;
flag |= Capabilities.CLIENT_FOUND_ROWS;
flag |= Capabilities.CLIENT_LONG_FLAG;
flag |= Capabilities.CLIENT_CONNECT_WITH_DB;
// flag |= Capabilities.CLIENT_NO_SCHEMA;
boolean usingCompress=MycatServer.getInstance().getConfig().getSystem().getUseCompression()==1 ;
if(usingCompress)
{
flag |= Capabilities.CLIENT_COMPRESS;
}
flag |= Capabilities.CLIENT_ODBC;
flag |= Capabilities.CLIENT_LOCAL_FILES;
flag |= Capabilities.CLIENT_IGNORE_SPACE;
flag |= Capabilities.CLIENT_PROTOCOL_41;
flag |= Capabilities.CLIENT_INTERACTIVE;
// flag |= Capabilities.CLIENT_SSL;
flag |= Capabilities.CLIENT_IGNORE_SIGPIPE;
flag |= Capabilities.CLIENT_TRANSACTIONS;
// flag |= Capabilities.CLIENT_RESERVED;
flag |= Capabilities.CLIENT_SECURE_CONNECTION;
// client extension
flag |= Capabilities.CLIENT_MULTI_STATEMENTS;
flag |= Capabilities.CLIENT_MULTI_RESULTS;
return flag;
}
private static final CommandPacket _READ_UNCOMMITTED = new CommandPacket();
private static final CommandPacket _READ_COMMITTED = new CommandPacket();
private static final CommandPacket _REPEATED_READ = new CommandPacket();
private static final CommandPacket _SERIALIZABLE = new CommandPacket();
private static final CommandPacket _AUTOCOMMIT_ON = new CommandPacket();
private static final CommandPacket _AUTOCOMMIT_OFF = new CommandPacket();
private static final CommandPacket _COMMIT = new CommandPacket();
private static final CommandPacket _ROLLBACK = new CommandPacket();
static {
_READ_UNCOMMITTED.packetId = 0;
_READ_UNCOMMITTED.command = MySQLPacket.COM_QUERY;
_READ_UNCOMMITTED.arg = "SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED"
.getBytes();
_READ_COMMITTED.packetId = 0;
_READ_COMMITTED.command = MySQLPacket.COM_QUERY;
_READ_COMMITTED.arg = "SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED"
.getBytes();
_REPEATED_READ.packetId = 0;
_REPEATED_READ.command = MySQLPacket.COM_QUERY;
_REPEATED_READ.arg = "SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ"
.getBytes();
_SERIALIZABLE.packetId = 0;
_SERIALIZABLE.command = MySQLPacket.COM_QUERY;
_SERIALIZABLE.arg = "SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE"
.getBytes();
_AUTOCOMMIT_ON.packetId = 0;
_AUTOCOMMIT_ON.command = MySQLPacket.COM_QUERY;
_AUTOCOMMIT_ON.arg = "SET autocommit=1".getBytes();
_AUTOCOMMIT_OFF.packetId = 0;
_AUTOCOMMIT_OFF.command = MySQLPacket.COM_QUERY;
_AUTOCOMMIT_OFF.arg = "SET autocommit=0".getBytes();
_COMMIT.packetId = 0;
_COMMIT.command = MySQLPacket.COM_QUERY;
_COMMIT.arg = "commit".getBytes();
_ROLLBACK.packetId = 0;
_ROLLBACK.command = MySQLPacket.COM_QUERY;
_ROLLBACK.arg = "rollback".getBytes();
}
private MySQLDataSource pool;
private boolean fromSlaveDB;
private long threadId;
private HandshakePacket handshake;
private volatile int txIsolation;
private volatile boolean autocommit;
private long clientFlags;
private boolean isAuthenticated;
private String user;
private String password;
private Object attachment;
private ResponseHandler respHandler;
private final AtomicBoolean isQuit;
private volatile StatusSync statusSync;
private volatile boolean metaDataSyned = true;
private volatile int xaStatus = 0;
public MySQLConnection(NetworkChannel channel, boolean fromSlaveDB) {
super(channel);
this.clientFlags = CLIENT_FLAGS;
this.lastTime = TimeUtil.currentTimeMillis();
this.isQuit = new AtomicBoolean(false);
this.autocommit = true;
this.fromSlaveDB = fromSlaveDB;
// 设为默认值,免得每个初始化好的连接都要去同步一下
this.txIsolation = MycatServer.getInstance().getConfig().getSystem().getTxIsolation();
}
public int getXaStatus() {
return xaStatus;
}
public void setXaStatus(int xaStatus) {
this.xaStatus = xaStatus;
}
public void onConnectFailed(Throwable t) {
if (handler instanceof MySQLConnectionHandler) {
MySQLConnectionHandler theHandler = (MySQLConnectionHandler) handler;
theHandler.connectionError(t);
} else {
((MySQLConnectionAuthenticator) handler).connectionError(this, t);
}
}
public String getSchema() {
return this.schema;
}
public void setSchema(String newSchema) {
String curSchema = schema;
if (curSchema == null) {
this.schema = newSchema;
this.oldSchema = newSchema;
} else {
this.oldSchema = curSchema;
this.schema = newSchema;
}
}
public MySQLDataSource getPool() {
return pool;
}
public void setPool(MySQLDataSource pool) {
this.pool = pool;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public void setPassword(String password) {
this.password = password;
}
public HandshakePacket getHandshake() {
return handshake;
}
public void setHandshake(HandshakePacket handshake) {
this.handshake = handshake;
}
public long getThreadId() {
return threadId;
}
public void setThreadId(long threadId) {
this.threadId = threadId;
}
public boolean isAuthenticated() {
return isAuthenticated;
}
public void setAuthenticated(boolean isAuthenticated) {
this.isAuthenticated = isAuthenticated;
}
public String getPassword() {
return password;
}
public void authenticate() {
AuthPacket packet = new AuthPacket();
packet.packetId = 1;
packet.clientFlags = clientFlags;
packet.maxPacketSize = maxPacketSize;
packet.charsetIndex = this.charsetIndex;
packet.user = user;
try {
packet.password = passwd(password, handshake);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e.getMessage());
}
packet.database = schema;
packet.write(this);
}
public boolean isAutocommit() {
return autocommit;
}
public Object getAttachment() {
return attachment;
}
public void setAttachment(Object attachment) {
this.attachment = attachment;
}
public boolean isClosedOrQuit() {
return isClosed() || isQuit.get();
}
protected void sendQueryCmd(String query) {
CommandPacket packet = new CommandPacket();
packet.packetId = 0;
packet.command = MySQLPacket.COM_QUERY;
try {
packet.arg = query.getBytes(charset);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
lastTime = TimeUtil.currentTimeMillis();
packet.write(this);
}
private static void getCharsetCommand(StringBuilder sb, int clientCharIndex) {
sb.append("SET names ").append(CharsetUtil.getCharset(clientCharIndex))
.append(";");
}
private static void getTxIsolationCommand(StringBuilder sb, int txIsolation) {
switch (txIsolation) {
case Isolations.READ_UNCOMMITTED:
sb.append("SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;");
return;
case Isolations.READ_COMMITTED:
sb.append("SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;");
return;
case Isolations.REPEATED_READ:
sb.append("SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;");
return;
case Isolations.SERIALIZABLE:
sb.append("SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;");
return;
default:
throw new UnknownTxIsolationException("txIsolation:" + txIsolation);
}
}
private void getAutocommitCommand(StringBuilder sb, boolean autoCommit) {
if (autoCommit) {
sb.append("SET autocommit=1;");
} else {
sb.append("SET autocommit=0;");
}
}
private static class StatusSync {
private final String schema;
private final Integer charsetIndex;
private final Integer txtIsolation;
private final Boolean autocommit;
private final AtomicInteger synCmdCount;
private final boolean xaStarted;
public StatusSync(boolean xaStarted, String schema,
Integer charsetIndex, Integer txtIsolation, Boolean autocommit,
int synCount) {
super();
this.xaStarted = xaStarted;
this.schema = schema;
this.charsetIndex = charsetIndex;
this.txtIsolation = txtIsolation;
this.autocommit = autocommit;
this.synCmdCount = new AtomicInteger(synCount);
}
public boolean synAndExecuted(MySQLConnection conn) {
int remains = synCmdCount.decrementAndGet();
if (remains == 0) {// syn command finished
this.updateConnectionInfo(conn);
conn.metaDataSyned = true;
return false;
} else if (remains < 0) {
return true;
}
return false;
}
private void updateConnectionInfo(MySQLConnection conn)
{
if (schema != null) {
conn.schema = schema;
conn.oldSchema = conn.schema;
}
if (charsetIndex != null) {
conn.setCharset(CharsetUtil.getCharset(charsetIndex));
}
if (txtIsolation != null) {
conn.txIsolation = txtIsolation;
}
if (autocommit != null) {
conn.autocommit = autocommit;
}
}
}
/**
* @return if synchronization finished and execute-sql has already been sent
* before
*/
public boolean syncAndExcute() {
StatusSync sync = this.statusSync;
if (sync == null) {
return true;
} else {
boolean executed = sync.synAndExecuted(this);
if (executed) {
statusSync = null;
}
return executed;
}
}
public void execute(RouteResultsetNode rrn, ServerConnection sc,
boolean autocommit) throws UnsupportedEncodingException {
if (!modifiedSQLExecuted && rrn.isModifySQL()) {
modifiedSQLExecuted = true;
}
String xaTXID = sc.getSession2().getXaTXID();
synAndDoExecute(xaTXID, rrn, sc.getCharsetIndex(), sc.getTxIsolation(),
autocommit);
}
private void synAndDoExecute(String xaTxID, RouteResultsetNode rrn,
int clientCharSetIndex, int clientTxIsoLation,
boolean clientAutoCommit) {
String xaCmd = null;
boolean conAutoComit = this.autocommit;
String conSchema = this.schema;
// never executed modify sql,so auto commit
boolean expectAutocommit = !modifiedSQLExecuted || isFromSlaveDB()
|| clientAutoCommit;
if (expectAutocommit == false && xaTxID != null && xaStatus == TxState.TX_INITIALIZE_STATE) {
//clientTxIsoLation = Isolations.SERIALIZABLE;
xaCmd = "XA START " + xaTxID + ';';
this.xaStatus = TxState.TX_STARTED_STATE;
}
int schemaSyn = conSchema.equals(oldSchema) ? 0 : 1;
int charsetSyn = 0;
if (this.charsetIndex != clientCharSetIndex) {
//need to syn the charset of connection.
//set current connection charset to client charset.
//otherwise while sending commend to server the charset will not coincidence.
setCharset(CharsetUtil.getCharset(clientCharSetIndex));
charsetSyn = 1;
}
int txIsoLationSyn = (txIsolation == clientTxIsoLation) ? 0 : 1;
int autoCommitSyn = (conAutoComit == expectAutocommit) ? 0 : 1;
int synCount = schemaSyn + charsetSyn + txIsoLationSyn + autoCommitSyn;
if (synCount == 0 && this.xaStatus != TxState.TX_STARTED_STATE) {
// not need syn connection
sendQueryCmd(rrn.getStatement());
return;
}
CommandPacket schemaCmd = null;
StringBuilder sb = new StringBuilder();
if (schemaSyn == 1) {
schemaCmd = getChangeSchemaCommand(conSchema);
// getChangeSchemaCommand(sb, conSchema);
}
if (charsetSyn == 1) {
getCharsetCommand(sb, clientCharSetIndex);
}
if (txIsoLationSyn == 1) {
getTxIsolationCommand(sb, clientTxIsoLation);
}
if (autoCommitSyn == 1) {
getAutocommitCommand(sb, expectAutocommit);
}
if (xaCmd != null) {
sb.append(xaCmd);
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("con need syn ,total syn cmd " + synCount
+ " commands " + sb.toString() + "schema change:"
+ (schemaCmd != null) + " con:" + this);
}
metaDataSyned = false;
statusSync = new StatusSync(xaCmd != null, conSchema,
clientCharSetIndex, clientTxIsoLation, expectAutocommit,
synCount);
// syn schema
if (schemaCmd != null) {
schemaCmd.write(this);
}
// and our query sql to multi command at last
sb.append(rrn.getStatement()+";");
// syn and execute others
this.sendQueryCmd(sb.toString());
// waiting syn result...
}
private static CommandPacket getChangeSchemaCommand(String schema) {
CommandPacket cmd = new CommandPacket();
cmd.packetId = 0;
cmd.command = MySQLPacket.COM_INIT_DB;
cmd.arg = schema.getBytes();
return cmd;
}
/**
* by wuzh ,execute a query and ignore transaction settings for performance
*
* @param sql
* @throws UnsupportedEncodingException
*/
public void query(String query) throws UnsupportedEncodingException {
RouteResultsetNode rrn = new RouteResultsetNode("default",
ServerParse.SELECT, query);
synAndDoExecute(null, rrn, this.charsetIndex, this.txIsolation, true);
}
public long getLastTime() {
return lastTime;
}
public void setLastTime(long lastTime) {
this.lastTime = lastTime;
}
public void quit() {
if (isQuit.compareAndSet(false, true) && !isClosed()) {
if (isAuthenticated) {
write(writeToBuffer(QuitPacket.QUIT, allocate()));
write(allocate());
} else {
close("normal");
}
}
}
@Override
public void close(String reason) {
if (!isClosed.get()) {
isQuit.set(true);
super.close(reason);
pool.connectionClosed(this);
if (this.respHandler != null) {
this.respHandler.connectionClose(this, reason);
respHandler = null;
}
}
}
public void commit() {
_COMMIT.write(this);
}
public boolean batchCmdFinished() {
batchCmdCount--;
return (batchCmdCount == 0);
}
public void execCmd(String cmd) {
this.sendQueryCmd(cmd);
}
public void execBatchCmd(String[] batchCmds) {
// "XA END "+xaID+";"+"XA PREPARE "+xaID
this.batchCmdCount = batchCmds.length;
StringBuilder sb = new StringBuilder();
for (String sql : batchCmds) {
sb.append(sql).append(';');
}
this.sendQueryCmd(sb.toString());
}
public void rollback() {
_ROLLBACK.write(this);
}
public void release() {
if (metaDataSyned == false) {// indicate connection not normalfinished
// ,and
// we can't know it's syn status ,so
// close
// it
LOGGER.warn("can't sure connection syn result,so close it " + this);
this.respHandler = null;
this.close("syn status unkown ");
return;
}
metaDataSyned = true;
attachment = null;
statusSync = null;
modifiedSQLExecuted = false;
setResponseHandler(null);
pool.releaseChannel(this);
}
public boolean setResponseHandler(ResponseHandler queryHandler) {
if (handler instanceof MySQLConnectionHandler) {
((MySQLConnectionHandler) handler).setResponseHandler(queryHandler);
respHandler = queryHandler;
return true;
} else if (queryHandler != null) {
LOGGER.warn("set not MySQLConnectionHandler "
+ queryHandler.getClass().getCanonicalName());
}
return false;
}
/**
* 写队列为空,可以继续写数据
*/
public void writeQueueAvailable() {
if (respHandler != null) {
respHandler.writeQueueAvailable();
}
}
/**
* 记录sql执行信息
*/
public void recordSql(String host, String schema, String stmt) {
// final long now = TimeUtil.currentTimeMillis();
// if (now > this.lastTime) {
// // long time = now - this.lastTime;
// // SQLRecorder sqlRecorder = this.pool.getSqlRecorder();
// // if (sqlRecorder.check(time)) {
// // SQLRecord recorder = new SQLRecord();
// // recorder.host = host;
// // recorder.schema = schema;
// // recorder.statement = stmt;
// // recorder.startTime = lastTime;
// // recorder.executeTime = time;
// // recorder.dataNode = pool.getName();
// // recorder.dataNodeIndex = pool.getIndex();
// // sqlRecorder.add(recorder);
// // }
// }
// this.lastTime = now;
}
private static byte[] passwd(String pass, HandshakePacket hs)
throws NoSuchAlgorithmException {
if (pass == null || pass.length() == 0) {
return null;
}
byte[] passwd = pass.getBytes();
int sl1 = hs.seed.length;
int sl2 = hs.restOfScrambleBuff.length;
byte[] seed = new byte[sl1 + sl2];
System.arraycopy(hs.seed, 0, seed, 0, sl1);
System.arraycopy(hs.restOfScrambleBuff, 0, seed, sl1, sl2);
return SecurityUtil.scramble411(passwd, seed);
}
@Override
public boolean isFromSlaveDB() {
return fromSlaveDB;
}
@Override
public boolean isBorrowed() {
return borrowed;
}
@Override
public void setBorrowed(boolean borrowed) {
this.lastTime = TimeUtil.currentTimeMillis();
this.borrowed = borrowed;
}
@Override
public String toString() {
return "MySQLConnection [id=" + id + ", lastTime=" + lastTime
+ ", user=" + user
+ ", schema=" + schema + ", old shema=" + oldSchema
+ ", borrowed=" + borrowed + ", fromSlaveDB=" + fromSlaveDB
+ ", threadId=" + threadId + ", charset=" + charset
+ ", txIsolation=" + txIsolation + ", autocommit=" + autocommit
+ ", attachment=" + attachment + ", respHandler=" + respHandler
+ ", host=" + host + ", port=" + port + ", statusSync="
+ statusSync + ", writeQueue=" + this.getWriteQueue().size()
+ ", modifiedSQLExecuted=" + modifiedSQLExecuted + "]";
}
@Override
public boolean isModifiedSQLExecuted() {
return modifiedSQLExecuted;
}
@Override
public int getTxIsolation() {
return txIsolation;
}
}
@@ -0,0 +1,150 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat.backend.mysql.nio;
import org.slf4j.Logger; import org.slf4j.LoggerFactory;
import io.mycat.MycatServer;
import io.mycat.backend.mysql.CharsetUtil;
import io.mycat.backend.mysql.SecurityUtil;
import io.mycat.backend.mysql.nio.handler.ResponseHandler;
import io.mycat.config.Capabilities;
import io.mycat.net.ConnectionException;
import io.mycat.net.NIOHandler;
import io.mycat.net.mysql.EOFPacket;
import io.mycat.net.mysql.ErrorPacket;
import io.mycat.net.mysql.HandshakePacket;
import io.mycat.net.mysql.OkPacket;
import io.mycat.net.mysql.Reply323Packet;
/**
* MySQL 验证处理器
*
* @author mycat
*/
public class MySQLConnectionAuthenticator implements NIOHandler {
private static final Logger LOGGER = LoggerFactory
.getLogger(MySQLConnectionAuthenticator.class);
private final MySQLConnection source;
private final ResponseHandler listener;
public MySQLConnectionAuthenticator(MySQLConnection source,
ResponseHandler listener) {
this.source = source;
this.listener = listener;
}
public void connectionError(MySQLConnection source, Throwable e) {
listener.connectionError(e, source);
}
@Override
public void handle(byte[] data) {
try {
switch (data[4]) {
case OkPacket.FIELD_COUNT:
HandshakePacket packet = source.getHandshake();
if (packet == null) {
processHandShakePacket(data);
// 发送认证数据包
source.authenticate();
break;
}
// 处理认证结果
source.setHandler(new MySQLConnectionHandler(source));
source.setAuthenticated(true);
boolean clientCompress = Capabilities.CLIENT_COMPRESS==(Capabilities.CLIENT_COMPRESS & packet.serverCapabilities);
boolean usingCompress= MycatServer.getInstance().getConfig().getSystem().getUseCompression()==1 ;
if(clientCompress&&usingCompress)
{
source.setSupportCompress(true);
}
if (listener != null) {
listener.connectionAcquired(source);
}
break;
case ErrorPacket.FIELD_COUNT:
ErrorPacket err = new ErrorPacket();
err.read(data);
String errMsg = new String(err.message);
LOGGER.warn("can't connect to mysql server ,errmsg:"+errMsg+" "+source);
//source.close(errMsg);
throw new ConnectionException(err.errno, errMsg);
case EOFPacket.FIELD_COUNT:
auth323(data[3]);
break;
default:
packet = source.getHandshake();
if (packet == null) {
processHandShakePacket(data);
// 发送认证数据包
source.authenticate();
break;
} else {
throw new RuntimeException("Unknown Packet!");
}
}
} catch (RuntimeException e) {
if (listener != null) {
listener.connectionError(e, source);
return;
}
throw e;
}
}
private void processHandShakePacket(byte[] data) {
// 设置握手数据包
HandshakePacket packet= new HandshakePacket();
packet.read(data);
source.setHandshake(packet);
source.setThreadId(packet.threadId);
// 设置字符集编码
int charsetIndex = (packet.serverCharsetIndex & 0xff);
String charset = CharsetUtil.getCharset(charsetIndex);
if (charset != null) {
source.setCharset(charset);
} else {
throw new RuntimeException("Unknown charsetIndex:" + charsetIndex);
}
}
private void auth323(byte packetId) {
// 发送323响应认证数据包
Reply323Packet r323 = new Reply323Packet();
r323.packetId = ++packetId;
String pass = source.getPassword();
if (pass != null && pass.length() > 0) {
byte[] seed = source.getHandshake().seed;
r323.seed = SecurityUtil.scramble323(pass, new String(seed))
.getBytes();
}
r323.write(source);
}
}
@@ -0,0 +1,73 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat.backend.mysql.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.nio.channels.NetworkChannel;
import io.mycat.MycatServer;
import io.mycat.backend.mysql.nio.handler.ResponseHandler;
import io.mycat.config.model.DBHostConfig;
import io.mycat.net.NIOConnector;
import io.mycat.net.factory.BackendConnectionFactory;
/**
* @author mycat
*/
public class MySQLConnectionFactory extends BackendConnectionFactory {
@SuppressWarnings({ "unchecked", "rawtypes" })
public MySQLConnection make(MySQLDataSource pool, ResponseHandler handler,
String schema) throws IOException {
DBHostConfig dsc = pool.getConfig();
NetworkChannel channel = openSocketChannel(MycatServer.getInstance()
.isAIO());
MySQLConnection c = new MySQLConnection(channel, pool.isReadNode());
MycatServer.getInstance().getConfig().setSocketParams(c, false);
c.setHost(dsc.getIp());
c.setPort(dsc.getPort());
c.setUser(dsc.getUser());
c.setPassword(dsc.getPassword());
c.setSchema(schema);
c.setHandler(new MySQLConnectionAuthenticator(c, handler));
c.setPool(pool);
c.setIdleTimeout(pool.getConfig().getIdleTimeout());
if (channel instanceof AsynchronousSocketChannel) {
((AsynchronousSocketChannel) channel).connect(
new InetSocketAddress(dsc.getIp(), dsc.getPort()), c,
(CompletionHandler) MycatServer.getInstance()
.getConnector());
} else {
((NIOConnector) MycatServer.getInstance().getConnector())
.postConnect(c);
}
return c;
}
}
@@ -0,0 +1,230 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat.backend.mysql.nio;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger; import org.slf4j.LoggerFactory;
import io.mycat.backend.mysql.ByteUtil;
import io.mycat.backend.mysql.nio.handler.LoadDataResponseHandler;
import io.mycat.backend.mysql.nio.handler.ResponseHandler;
import io.mycat.net.handler.BackendAsyncHandler;
import io.mycat.net.mysql.EOFPacket;
import io.mycat.net.mysql.ErrorPacket;
import io.mycat.net.mysql.OkPacket;
import io.mycat.net.mysql.RequestFilePacket;
/**
* life cycle: from connection establish to close <br/>
*
* @author mycat
*/
public class MySQLConnectionHandler extends BackendAsyncHandler {
private static final Logger logger = LoggerFactory
.getLogger(MySQLConnectionHandler.class);
private static final int RESULT_STATUS_INIT = 0;
private static final int RESULT_STATUS_HEADER = 1;
private static final int RESULT_STATUS_FIELD_EOF = 2;
private final MySQLConnection source;
private volatile int resultStatus;
private volatile byte[] header;
private volatile List<byte[]> fields;
/**
* life cycle: one SQL execution
*/
private volatile ResponseHandler responseHandler;
public MySQLConnectionHandler(MySQLConnection source) {
this.source = source;
this.resultStatus = RESULT_STATUS_INIT;
}
public void connectionError(Throwable e) {
if (responseHandler != null) {
responseHandler.connectionError(e, source);
}
}
public MySQLConnection getSource() {
return source;
}
@Override
public void handle(byte[] data) {
offerData(data, source.getProcessor().getExecutor());
}
@Override
protected void offerDataError() {
resultStatus = RESULT_STATUS_INIT;
throw new RuntimeException("offer data error!");
}
@Override
protected void handleData(byte[] data) {
switch (resultStatus) {
case RESULT_STATUS_INIT:
switch (data[4]) {
case OkPacket.FIELD_COUNT:
handleOkPacket(data);
break;
case ErrorPacket.FIELD_COUNT:
handleErrorPacket(data);
break;
case RequestFilePacket.FIELD_COUNT:
handleRequestPacket(data);
break;
default:
resultStatus = RESULT_STATUS_HEADER;
header = data;
fields = new ArrayList<byte[]>((int) ByteUtil.readLength(data,
4));
}
break;
case RESULT_STATUS_HEADER:
switch (data[4]) {
case ErrorPacket.FIELD_COUNT:
resultStatus = RESULT_STATUS_INIT;
handleErrorPacket(data);
break;
case EOFPacket.FIELD_COUNT:
resultStatus = RESULT_STATUS_FIELD_EOF;
handleFieldEofPacket(data);
break;
default:
fields.add(data);
}
break;
case RESULT_STATUS_FIELD_EOF:
switch (data[4]) {
case ErrorPacket.FIELD_COUNT:
resultStatus = RESULT_STATUS_INIT;
handleErrorPacket(data);
break;
case EOFPacket.FIELD_COUNT:
resultStatus = RESULT_STATUS_INIT;
handleRowEofPacket(data);
break;
default:
handleRowPacket(data);
}
break;
default:
throw new RuntimeException("unknown status!");
}
}
public void setResponseHandler(ResponseHandler responseHandler) {
// logger.info("set response handler "+responseHandler);
// if (this.responseHandler != null && responseHandler != null) {
// throw new RuntimeException("reset agani!");
// }
this.responseHandler = responseHandler;
}
/**
* OK数据包处理
*/
private void handleOkPacket(byte[] data) {
ResponseHandler respHand = responseHandler;
if (respHand != null) {
respHand.okResponse(data, source);
}
}
/**
* ERROR数据包处理
*/
private void handleErrorPacket(byte[] data) {
ResponseHandler respHand = responseHandler;
if (respHand != null) {
respHand.errorResponse(data, source);
} else {
closeNoHandler();
}
}
/**
* load data file 请求文件数据包处理
*/
private void handleRequestPacket(byte[] data) {
ResponseHandler respHand = responseHandler;
if (respHand != null && respHand instanceof LoadDataResponseHandler) {
((LoadDataResponseHandler) respHand).requestDataResponse(data,
source);
} else {
closeNoHandler();
}
}
/**
* 字段数据包结束处理
*/
private void handleFieldEofPacket(byte[] data) {
ResponseHandler respHand = responseHandler;
if (respHand != null) {
respHand.fieldEofResponse(header, fields, data, source);
} else {
closeNoHandler();
}
}
/**
* 行数据包处理
*/
private void handleRowPacket(byte[] data) {
ResponseHandler respHand = responseHandler;
if (respHand != null) {
respHand.rowResponse(data, source);
} else {
closeNoHandler();
}
}
private void closeNoHandler() {
if (!source.isClosedOrQuit()) {
source.close("no handler");
logger.warn("no handler bind in this con " + this + " client:"
+ source);
}
}
/**
* 行数据包结束处理
*/
private void handleRowEofPacket(byte[] data) {
if (responseHandler != null) {
responseHandler.rowEofResponse(data, source);
} else {
closeNoHandler();
}
}
}
@@ -0,0 +1,210 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat.backend.mysql.nio;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.security.NoSuchAlgorithmException;
import io.mycat.backend.datasource.PhysicalDatasource;
import io.mycat.backend.heartbeat.DBHeartbeat;
import io.mycat.backend.heartbeat.MySQLHeartbeat;
import io.mycat.backend.mysql.SecurityUtil;
import io.mycat.backend.mysql.nio.handler.ResponseHandler;
import io.mycat.config.Capabilities;
import io.mycat.config.model.DBHostConfig;
import io.mycat.config.model.DataHostConfig;
import io.mycat.net.mysql.AuthPacket;
import io.mycat.net.mysql.BinaryPacket;
import io.mycat.net.mysql.EOFPacket;
import io.mycat.net.mysql.ErrorPacket;
import io.mycat.net.mysql.HandshakePacket;
import io.mycat.net.mysql.OkPacket;
import io.mycat.net.mysql.QuitPacket;
import io.mycat.net.mysql.Reply323Packet;
/**
* @author mycat
*/
public class MySQLDataSource extends PhysicalDatasource {
private final MySQLConnectionFactory factory;
public MySQLDataSource(DBHostConfig config, DataHostConfig hostConfig,
boolean isReadNode) {
super(config, hostConfig, isReadNode);
this.factory = new MySQLConnectionFactory();
}
@Override
public void createNewConnection(ResponseHandler handler,String schema) throws IOException {
factory.make(this, handler,schema);
}
private long getClientFlags() {
int flag = 0;
flag |= Capabilities.CLIENT_LONG_PASSWORD;
flag |= Capabilities.CLIENT_FOUND_ROWS;
flag |= Capabilities.CLIENT_LONG_FLAG;
flag |= Capabilities.CLIENT_CONNECT_WITH_DB;
// flag |= Capabilities.CLIENT_NO_SCHEMA;
// flag |= Capabilities.CLIENT_COMPRESS;
flag |= Capabilities.CLIENT_ODBC;
// flag |= Capabilities.CLIENT_LOCAL_FILES;
flag |= Capabilities.CLIENT_IGNORE_SPACE;
flag |= Capabilities.CLIENT_PROTOCOL_41;
flag |= Capabilities.CLIENT_INTERACTIVE;
// flag |= Capabilities.CLIENT_SSL;
flag |= Capabilities.CLIENT_IGNORE_SIGPIPE;
flag |= Capabilities.CLIENT_TRANSACTIONS;
// flag |= Capabilities.CLIENT_RESERVED;
flag |= Capabilities.CLIENT_SECURE_CONNECTION;
// client extension
// flag |= Capabilities.CLIENT_MULTI_STATEMENTS;
// flag |= Capabilities.CLIENT_MULTI_RESULTS;
return flag;
}
private byte[] passwd(String pass, HandshakePacket hs) throws NoSuchAlgorithmException {
if (pass == null || pass.length() == 0) {
return null;
}
byte[] passwd = pass.getBytes();
int sl1 = hs.seed.length;
int sl2 = hs.restOfScrambleBuff.length;
byte[] seed = new byte[sl1 + sl2];
System.arraycopy(hs.seed, 0, seed, 0, sl1);
System.arraycopy(hs.restOfScrambleBuff, 0, seed, sl1, sl2);
return SecurityUtil.scramble411(passwd, seed);
}
@Override
public boolean testConnection(String schema) throws IOException {
boolean isConnected = true;
Socket socket = null;
InputStream in = null;
OutputStream out = null;
try {
socket = new Socket(this.getConfig().getIp(), this.getConfig().getPort());
socket.setSoTimeout(1000 * 20);
socket.setReceiveBufferSize( 32768 );
socket.setSendBufferSize( 32768 );
socket.setTcpNoDelay(true);
socket.setKeepAlive(true);
in = new BufferedInputStream(socket.getInputStream(), 32768);
out = new BufferedOutputStream( socket.getOutputStream(), 32768 );
/**
* Phase 1: MySQL to client. Send handshake packet.
*/
BinaryPacket bin1 = new BinaryPacket();
bin1.read(in);
HandshakePacket handshake = new HandshakePacket();
handshake.read( bin1 );
/**
* Phase 2: client to MySQL. Send auth packet.
*/
AuthPacket authPacket = new AuthPacket();
authPacket.packetId = 1;
authPacket.clientFlags = getClientFlags();
authPacket.maxPacketSize = 1024 * 1024 * 16;
authPacket.charsetIndex = handshake.serverCharsetIndex & 0xff;
authPacket.user = this.getConfig().getUser();;
try {
authPacket.password = passwd(this.getConfig().getPassword(), handshake);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e.getMessage());
}
authPacket.database = schema;
authPacket.write(out);
out.flush();
/**
* Phase 3: MySQL to client. send OK/ERROR packet.
*/
BinaryPacket bin2 = new BinaryPacket();
bin2.read(in);
switch (bin2.data[0]) {
case OkPacket.FIELD_COUNT:
break;
case ErrorPacket.FIELD_COUNT:
ErrorPacket err = new ErrorPacket();
err.read(bin2);
isConnected = false;
case EOFPacket.FIELD_COUNT:
// 发送323响应认证数据包
Reply323Packet r323 = new Reply323Packet();
r323.packetId = ++bin2.packetId;
String passwd = this.getConfig().getPassword();
if (passwd != null && passwd.length() > 0) {
r323.seed = SecurityUtil.scramble323(passwd, new String(handshake.seed)).getBytes();
}
r323.write(out);
out.flush();
break;
}
} catch (IOException e) {
isConnected = false;
} finally {
try {
if (in != null) {
in.close();
}
} catch (IOException e) {}
try {
if (out != null) {
out.write(QuitPacket.QUIT);
out.flush();
out.close();
}
} catch (IOException e) {}
try {
if (socket != null)
socket.close();
} catch (IOException e) {}
}
return isConnected;
}
@Override
public DBHeartbeat createHeartBeat() {
return new MySQLHeartbeat(this);
}
}
@@ -0,0 +1,162 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat.backend.mysql.nio.handler;
import java.util.List;
import io.mycat.backend.mysql.xa.TxState;
import io.mycat.config.ErrorCode;
import org.slf4j.Logger; import org.slf4j.LoggerFactory;
import io.mycat.backend.BackendConnection;
import io.mycat.backend.mysql.nio.MySQLConnection;
import io.mycat.net.mysql.ErrorPacket;
import io.mycat.server.NonBlockingSession;
import io.mycat.server.ServerConnection;
/**
* @author mycat
*/
public class CommitNodeHandler implements ResponseHandler {
private static final Logger LOGGER = LoggerFactory
.getLogger(CommitNodeHandler.class);
private final NonBlockingSession session;
public CommitNodeHandler(NonBlockingSession session) {
this.session = session;
}
public void commit(BackendConnection conn) {
conn.setResponseHandler(CommitNodeHandler.this);
boolean isClosed=conn.isClosedOrQuit();
if(isClosed)
{
session.getSource().writeErrMessage(ErrorCode.ER_UNKNOWN_ERROR,
"receive commit,but find backend con is closed or quit");
LOGGER.error( conn+"receive commit,but fond backend con is closed or quit");
}
if(conn instanceof MySQLConnection)
{
MySQLConnection mysqlCon = (MySQLConnection) conn;
if (mysqlCon.getXaStatus() == 1)
{
String xaTxId = session.getXaTXID();
String[] cmds = new String[]{"XA END " + xaTxId,
"XA PREPARE " + xaTxId};
mysqlCon.execBatchCmd(cmds);
} else
{
conn.commit();
}
}else
{
conn.commit();
}
}
@Override
public void connectionAcquired(BackendConnection conn) {
LOGGER.error("unexpected invocation: connectionAcquired from commit");
}
@Override
public void okResponse(byte[] ok, BackendConnection conn) {
if(conn instanceof MySQLConnection)
{
MySQLConnection mysqlCon = (MySQLConnection) conn;
switch (mysqlCon.getXaStatus())
{
case 1:
if (mysqlCon.batchCmdFinished())
{
String xaTxId = session.getXaTXID();
mysqlCon.execCmd("XA COMMIT " + xaTxId);
mysqlCon.setXaStatus(TxState.TX_PREPARED_STATE);
}
return;
case 2:
{
mysqlCon.setXaStatus(TxState.TX_INITIALIZE_STATE);
break;
}
default:
// LOGGER.error("Wrong XA status flag!");
}
}
session.clearResources(false);
ServerConnection source = session.getSource();
source.write(ok);
}
@Override
public void errorResponse(byte[] err, BackendConnection conn) {
ErrorPacket errPkg = new ErrorPacket();
errPkg.read(err);
String errInfo = new String(errPkg.message);
session.getSource().setTxInterrupt(errInfo);
errPkg.write(session.getSource());
}
@Override
public void rowEofResponse(byte[] eof, BackendConnection conn) {
LOGGER.error(new StringBuilder().append("unexpected packet for ")
.append(conn).append(" bound by ").append(session.getSource())
.append(": field's eof").toString());
}
@Override
public void fieldEofResponse(byte[] header, List<byte[]> fields,
byte[] eof, BackendConnection conn) {
LOGGER.error(new StringBuilder().append("unexpected packet for ")
.append(conn).append(" bound by ").append(session.getSource())
.append(": field's eof").toString());
}
@Override
public void rowResponse(byte[] row, BackendConnection conn) {
LOGGER.warn(new StringBuilder().append("unexpected packet for ")
.append(conn).append(" bound by ").append(session.getSource())
.append(": row data packet").toString());
}
@Override
public void writeQueueAvailable() {
}
@Override
public void connectionError(Throwable e, BackendConnection conn) {
}
@Override
public void connectionClose(BackendConnection conn, String reason) {
}
}
@@ -0,0 +1,187 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat.backend.mysql.nio.handler;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
import org.slf4j.Logger; import org.slf4j.LoggerFactory;
import io.mycat.backend.BackendConnection;
import io.mycat.net.mysql.ErrorPacket;
/**
* heartbeat check for mysql connections
*
* @author wuzhih
*
*/
public class ConnectionHeartBeatHandler implements ResponseHandler {
private static final Logger LOGGER = LoggerFactory
.getLogger(ConnectionHeartBeatHandler.class);
protected final ReentrantLock lock = new ReentrantLock();
private final ConcurrentHashMap<Long, HeartBeatCon> allCons = new ConcurrentHashMap<Long, HeartBeatCon>();
public void doHeartBeat(BackendConnection conn, String sql) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("do heartbeat for con " + conn);
}
try {
HeartBeatCon hbCon = new HeartBeatCon(conn);
boolean notExist = (allCons.putIfAbsent(hbCon.conn.getId(), hbCon) == null);
if (notExist) {
conn.setResponseHandler(this);
conn.query(sql);
}
} catch (Exception e) {
executeException(conn, e);
}
}
/**
* remove timeout connections
*/
public void abandTimeOuttedConns() {
if (allCons.isEmpty()) {
return;
}
Collection<BackendConnection> abandCons = new LinkedList<BackendConnection>();
long curTime = System.currentTimeMillis();
Iterator<Entry<Long, HeartBeatCon>> itors = allCons.entrySet()
.iterator();
while (itors.hasNext()) {
HeartBeatCon hbCon = itors.next().getValue();
if (hbCon.timeOutTimestamp < curTime) {
abandCons.add(hbCon.conn);
itors.remove();
}
}
if (!abandCons.isEmpty()) {
for (BackendConnection con : abandCons) {
try {
// if(con.isBorrowed())
con.close("heartbeat timeout ");
} catch (Exception e) {
LOGGER.warn("close err:" + e);
}
}
}
}
@Override
public void connectionAcquired(BackendConnection conn) {
// not called
}
@Override
public void connectionError(Throwable e, BackendConnection conn) {
// not called
}
@Override
public void errorResponse(byte[] data, BackendConnection conn) {
removeFinished(conn);
ErrorPacket err = new ErrorPacket();
err.read(data);
LOGGER.warn("errorResponse " + err.errno + " "
+ new String(err.message));
conn.release();
}
@Override
public void okResponse(byte[] ok, BackendConnection conn) {
boolean executeResponse = conn.syncAndExcute();
if (executeResponse) {
removeFinished(conn);
conn.release();
}
}
@Override
public void rowResponse(byte[] row, BackendConnection conn) {
}
@Override
public void rowEofResponse(byte[] eof, BackendConnection conn) {
removeFinished(conn);
conn.release();
}
private void executeException(BackendConnection c, Throwable e) {
removeFinished(c);
LOGGER.warn("executeException ", e);
c.close("heatbeat exception:" + e);
}
private void removeFinished(BackendConnection con) {
Long id = ((BackendConnection) con).getId();
this.allCons.remove(id);
}
@Override
public void writeQueueAvailable() {
}
@Override
public void connectionClose(BackendConnection conn, String reason) {
removeFinished(conn);
LOGGER.warn("connection closed " + conn + " reason:" + reason);
}
@Override
public void fieldEofResponse(byte[] header, List<byte[]> fields,
byte[] eof, BackendConnection conn) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("received field eof from " + conn);
}
}
}
class HeartBeatCon {
public final long timeOutTimestamp;
public final BackendConnection conn;
public HeartBeatCon(BackendConnection conn) {
super();
this.timeOutTimestamp = System.currentTimeMillis() + 20 * 1000L;
this.conn = conn;
}
}
@@ -0,0 +1,90 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat.backend.mysql.nio.handler;
import java.util.List;
import io.mycat.backend.BackendConnection;
/**
* @author mycat
*/
public class DelegateResponseHandler implements ResponseHandler {
private final ResponseHandler target;
public DelegateResponseHandler(ResponseHandler target) {
if (target == null) {
throw new IllegalArgumentException("delegate is null!");
}
this.target = target;
}
@Override
public void connectionAcquired(BackendConnection conn) {
target.connectionAcquired(conn);
}
@Override
public void connectionError(Throwable e, BackendConnection conn) {
target.connectionError(e, conn);
}
@Override
public void okResponse(byte[] ok, BackendConnection conn) {
target.okResponse(ok, conn);
}
@Override
public void errorResponse(byte[] err, BackendConnection conn) {
target.errorResponse(err, conn);
}
@Override
public void fieldEofResponse(byte[] header, List<byte[]> fields, byte[] eof, BackendConnection conn) {
target.fieldEofResponse(header, fields, eof, conn);
}
@Override
public void rowResponse(byte[] row, BackendConnection conn) {
target.rowResponse(row, conn);
}
@Override
public void rowEofResponse(byte[] eof, BackendConnection conn) {
target.rowEofResponse(eof, conn);
}
@Override
public void writeQueueAvailable() {
target.writeQueueAvailable();
}
@Override
public void connectionClose(BackendConnection conn, String reason) {
target.connectionClose(conn, reason);
}
}
@@ -0,0 +1,210 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat.backend.mysql.nio.handler;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import org.slf4j.Logger; import org.slf4j.LoggerFactory;
import io.mycat.MycatServer;
import io.mycat.backend.BackendConnection;
import io.mycat.backend.ConnectionMeta;
import io.mycat.backend.datasource.PhysicalDBNode;
import io.mycat.cache.CachePool;
import io.mycat.config.MycatConfig;
import io.mycat.net.mysql.ErrorPacket;
import io.mycat.net.mysql.RowDataPacket;
import io.mycat.route.RouteResultsetNode;
import io.mycat.server.parser.ServerParse;
/**
* company where id=(select company_id from customer where id=3); the one which
* return data (id) is the datanode to store child table's records
*
* @author wuzhih
*
*/
public class FetchStoreNodeOfChildTableHandler implements ResponseHandler {
private static final Logger LOGGER = LoggerFactory
.getLogger(FetchStoreNodeOfChildTableHandler.class);
private String sql;
private volatile String result;
private volatile String dataNode;
private AtomicInteger finished = new AtomicInteger(0);
protected final ReentrantLock lock = new ReentrantLock();
public String execute(String schema, String sql, ArrayList<String> dataNodes) {
String key = schema + ":" + sql;
CachePool cache = MycatServer.getInstance().getCacheService()
.getCachePool("ER_SQL2PARENTID");
String result = (String) cache.get(key);
if (result != null) {
return result;
}
this.sql = sql;
int totalCount = dataNodes.size();
long startTime = System.currentTimeMillis();
long endTime = startTime + 5 * 60 * 1000L;
MycatConfig conf = MycatServer.getInstance().getConfig();
LOGGER.debug("find child node with sql:" + sql);
for (String dn : dataNodes) {
if (dataNode != null) {
return dataNode;
}
PhysicalDBNode mysqlDN = conf.getDataNodes().get(dn);
try {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("execute in datanode " + dn);
}
RouteResultsetNode node = new RouteResultsetNode(dn, ServerParse.SELECT, sql);
node.setRunOnSlave(false); // 获取 子表节点,最好走master为好
mysqlDN.getConnection(mysqlDN.getDatabase(), true, node, this, dn);
// mysqlDN.getConnection(mysqlDN.getDatabase(), true,
// new RouteResultsetNode(dn, ServerParse.SELECT, sql),
// this, dn);
} catch (Exception e) {
LOGGER.warn("get connection err " + e);
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
}
while (dataNode == null && System.currentTimeMillis() < endTime) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
break;
}
if (dataNode != null || finished.get() >= totalCount) {
break;
}
}
if (dataNode != null) {
cache.putIfAbsent(key, dataNode);
}
return dataNode;
}
@Override
public void connectionAcquired(BackendConnection conn) {
conn.setResponseHandler(this);
try {
conn.query(sql);
} catch (Exception e) {
executeException(conn, e);
}
}
@Override
public void connectionError(Throwable e, BackendConnection conn) {
finished.incrementAndGet();
LOGGER.warn("connectionError " + e);
}
@Override
public void errorResponse(byte[] data, BackendConnection conn) {
finished.incrementAndGet();
ErrorPacket err = new ErrorPacket();
err.read(data);
LOGGER.warn("errorResponse " + err.errno + " "
+ new String(err.message));
conn.release();
}
@Override
public void okResponse(byte[] ok, BackendConnection conn) {
boolean executeResponse = conn.syncAndExcute();
if (executeResponse) {
finished.incrementAndGet();
conn.release();
}
}
@Override
public void rowResponse(byte[] row, BackendConnection conn) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("received rowResponse response," + getColumn(row)
+ " from " + conn);
}
if (result == null) {
result = getColumn(row);
dataNode = (String) conn.getAttachment();
} else {
LOGGER.warn("find multi data nodes for child table store, sql is: "
+ sql);
}
}
private String getColumn(byte[] row) {
RowDataPacket rowDataPkg = new RowDataPacket(1);
rowDataPkg.read(row);
byte[] columnData = rowDataPkg.fieldValues.get(0);
return new String(columnData);
}
@Override
public void rowEofResponse(byte[] eof, BackendConnection conn) {
finished.incrementAndGet();
conn.release();
}
private void executeException(BackendConnection c, Throwable e) {
finished.incrementAndGet();
LOGGER.warn("executeException " + e);
c.close("exception:" + e);
}
@Override
public void writeQueueAvailable() {
}
@Override
public void connectionClose(BackendConnection conn, String reason) {
LOGGER.warn("connection closed " + conn + " reason:" + reason);
}
@Override
public void fieldEofResponse(byte[] header, List<byte[]> fields,
byte[] eof, BackendConnection conn) {
}
}
@@ -0,0 +1,116 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat.backend.mysql.nio.handler;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
import org.slf4j.Logger; import org.slf4j.LoggerFactory;
import io.mycat.backend.BackendConnection;
/**
* wuzh
*
* @author mycat
*
*/
public class GetConnectionHandler implements ResponseHandler {
private final CopyOnWriteArrayList<BackendConnection> successCons;
private static final Logger logger = LoggerFactory
.getLogger(GetConnectionHandler.class);
private final AtomicInteger finishedCount = new AtomicInteger(0);
private final int total;
public GetConnectionHandler(
CopyOnWriteArrayList<BackendConnection> connsToStore,
int totalNumber) {
super();
this.successCons = connsToStore;
this.total = totalNumber;
}
public String getStatusInfo()
{
return "finished "+ finishedCount.get()+" success "+successCons.size()+" target count:"+this.total;
}
public boolean finished() {
return finishedCount.get() >= total;
}
@Override
public void connectionAcquired(BackendConnection conn) {
successCons.add(conn);
finishedCount.addAndGet(1);
logger.info("connected successfuly " + conn);
conn.release();
}
@Override
public void connectionError(Throwable e, BackendConnection conn) {
finishedCount.addAndGet(1);
logger.warn("connect error " + conn+ e);
conn.release();
}
@Override
public void errorResponse(byte[] err, BackendConnection conn) {
logger.warn("caught error resp: " + conn + " " + new String(err));
conn.release();
}
@Override
public void okResponse(byte[] ok, BackendConnection conn) {
logger.info("received ok resp: " + conn + " " + new String(ok));
}
@Override
public void fieldEofResponse(byte[] header, List<byte[]> fields,
byte[] eof, BackendConnection conn) {
}
@Override
public void rowResponse(byte[] row, BackendConnection conn) {
}
@Override
public void rowEofResponse(byte[] eof, BackendConnection conn) {
}
@Override
public void writeQueueAvailable() {
}
@Override
public void connectionClose(BackendConnection conn, String reason) {
}
}
@@ -0,0 +1,125 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat.backend.mysql.nio.handler;
import java.io.UnsupportedEncodingException;
import java.util.List;
import org.slf4j.Logger; import org.slf4j.LoggerFactory;
import io.mycat.backend.BackendConnection;
import io.mycat.backend.mysql.nio.MySQLConnection;
import io.mycat.net.mysql.CommandPacket;
import io.mycat.net.mysql.ErrorPacket;
import io.mycat.net.mysql.MySQLPacket;
import io.mycat.server.NonBlockingSession;
/**
* @author mycat
*/
public class KillConnectionHandler implements ResponseHandler {
private static final Logger LOGGER = LoggerFactory
.getLogger(KillConnectionHandler.class);
private final MySQLConnection killee;
private final NonBlockingSession session;
public KillConnectionHandler(BackendConnection killee,
NonBlockingSession session) {
this.killee = (MySQLConnection) killee;
this.session = session;
}
@Override
public void connectionAcquired(BackendConnection conn) {
MySQLConnection mysqlCon = (MySQLConnection) conn;
conn.setResponseHandler(this);
CommandPacket packet = new CommandPacket();
packet.packetId = 0;
packet.command = MySQLPacket.COM_QUERY;
packet.arg = new StringBuilder("KILL ").append(killee.getThreadId())
.toString().getBytes();
packet.write(mysqlCon);
}
@Override
public void connectionError(Throwable e, BackendConnection conn) {
killee.close("exception:" + e.toString());
}
@Override
public void okResponse(byte[] ok, BackendConnection conn) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("kill connection success connection id:"
+ killee.getThreadId());
}
conn.release();
killee.close("killed");
}
@Override
public void rowEofResponse(byte[] eof, BackendConnection conn) {
LOGGER.warn(new StringBuilder().append("unexpected packet for ")
.append(conn).append(" bound by ").append(session.getSource())
.append(": field's eof").toString());
conn.quit();
killee.close("killed");
}
@Override
public void errorResponse(byte[] data, BackendConnection conn) {
ErrorPacket err = new ErrorPacket();
err.read(data);
String msg = null;
try {
msg = new String(err.message, conn.getCharset());
} catch (UnsupportedEncodingException e) {
msg = new String(err.message);
}
LOGGER.warn("kill backend connection " + killee + " failed: " + msg
+ " con:" + conn);
conn.release();
killee.close("exception:" + msg);
}
@Override
public void fieldEofResponse(byte[] header, List<byte[]> fields,
byte[] eof, BackendConnection conn) {
}
@Override
public void rowResponse(byte[] row, BackendConnection conn) {
}
@Override
public void writeQueueAvailable() {
}
@Override
public void connectionClose(BackendConnection conn, String reason) {
}
}
@@ -0,0 +1,14 @@
package io.mycat.backend.mysql.nio.handler;
import io.mycat.backend.BackendConnection;
/**
* Created by nange on 2015/3/31.
*/
public interface LoadDataResponseHandler
{
/**
* 收到请求发送文件数据包的响应处理
*/
void requestDataResponse(byte[] row, BackendConnection conn);
}
@@ -0,0 +1,135 @@
package io.mycat.backend.mysql.nio.handler;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.mycat.MycatServer;
import io.mycat.backend.BackendConnection;
import io.mycat.backend.datasource.PhysicalDBNode;
import io.mycat.config.MycatConfig;
import io.mycat.net.mysql.OkPacket;
import io.mycat.route.RouteResultset;
import io.mycat.route.RouteResultsetNode;
import io.mycat.server.NonBlockingSession;
/**
* lock tables 语句处理器
* @author songdabin
*
*/
public class LockTablesHandler extends MultiNodeHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(LockTablesHandler.class);
private final RouteResultset rrs;
private final ReentrantLock lock;
private final boolean autocommit;
public LockTablesHandler(NonBlockingSession session, RouteResultset rrs) {
super(session);
this.rrs = rrs;
this.autocommit = session.getSource().isAutocommit();
this.lock = new ReentrantLock();
}
public void execute() throws Exception {
super.reset(this.rrs.getNodes().length);
MycatConfig conf = MycatServer.getInstance().getConfig();
for (final RouteResultsetNode node : rrs.getNodes()) {
BackendConnection conn = session.getTarget(node);
if (session.tryExistsCon(conn, node)) {
_execute(conn, node);
} else {
// create new connection
PhysicalDBNode dn = conf.getDataNodes().get(node.getName());
dn.getConnection(dn.getDatabase(), autocommit, node, this, node);
}
}
}
private void _execute(BackendConnection conn, RouteResultsetNode node) {
if (clearIfSessionClosed(session)) {
return;
}
conn.setResponseHandler(this);
try {
conn.execute(node, session.getSource(), autocommit);
} catch (IOException e) {
connectionError(e, conn);
}
}
@Override
public void connectionAcquired(BackendConnection conn) {
final RouteResultsetNode node = (RouteResultsetNode) conn.getAttachment();
session.bindConnection(node, conn);
_execute(conn, node);
}
@Override
public void okResponse(byte[] data, BackendConnection conn) {
boolean executeResponse = conn.syncAndExcute();
if (executeResponse) {
if (clearIfSessionClosed(session)) {
return;
}
boolean isEndPack = decrementCountBy(1);
if (isEndPack) {
if (this.isFail() || session.closed()) {
tryErrorFinished(true);
return;
}
OkPacket ok = new OkPacket();
ok.read(data);
lock.lock();
try {
ok.packetId = ++ packetId;
ok.serverStatus = session.getSource().isAutocommit() ? 2:1;
} finally {
lock.unlock();
}
ok.write(session.getSource());
}
}
}
protected String byte2Str(byte[] data) {
StringBuilder sb = new StringBuilder();
for (byte b : data) {
sb.append(Byte.toString(b));
}
return sb.toString();
}
@Override
public void fieldEofResponse(byte[] header, List<byte[]> fields, byte[] eof, BackendConnection conn) {
LOGGER.error(new StringBuilder().append("unexpected packet for ")
.append(conn).append(" bound by ").append(session.getSource())
.append(": field's eof").toString());
}
@Override
public void rowResponse(byte[] row, BackendConnection conn) {
LOGGER.warn(new StringBuilder().append("unexpected packet for ")
.append(conn).append(" bound by ").append(session.getSource())
.append(": row data packet").toString());
}
@Override
public void rowEofResponse(byte[] eof, BackendConnection conn) {
LOGGER.error(new StringBuilder().append("unexpected packet for ")
.append(conn).append(" bound by ").append(session.getSource())
.append(": row's eof").toString());
}
@Override
public void writeQueueAvailable() {
// TODO Auto-generated method stub
}
}
@@ -0,0 +1,235 @@
package io.mycat.backend.mysql.nio.handler;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import io.mycat.backend.mysql.nio.MySQLConnection;
import io.mycat.backend.mysql.xa.CoordinatorLogEntry;
import io.mycat.backend.mysql.xa.ParticipantLogEntry;
import io.mycat.backend.mysql.xa.TxState;
import io.mycat.backend.mysql.xa.recovery.Repository;
import io.mycat.backend.mysql.xa.recovery.impl.FileSystemRepository;
import io.mycat.backend.mysql.xa.recovery.impl.InMemoryRepository;
import org.slf4j.Logger; import org.slf4j.LoggerFactory;
import io.mycat.backend.BackendConnection;
import io.mycat.route.RouteResultsetNode;
import io.mycat.server.NonBlockingSession;
import io.mycat.server.sqlcmd.SQLCtrlCommand;
public class MultiNodeCoordinator implements ResponseHandler {
private static final Logger LOGGER = LoggerFactory
.getLogger(MultiNodeCoordinator.class);
public static final Repository fileRepository = new FileSystemRepository();
public static final Repository inMemoryRepository = new InMemoryRepository();
private final AtomicInteger runningCount = new AtomicInteger(0);
private final AtomicInteger faileCount = new AtomicInteger(0);
private volatile int nodeCount;
private final NonBlockingSession session;
private SQLCtrlCommand cmdHandler;
private final AtomicBoolean failed = new AtomicBoolean(false);
public MultiNodeCoordinator(NonBlockingSession session) {
this.session = session;
}
/** Multi-nodes 1pc Commit Handle **/
public void executeBatchNodeCmd(SQLCtrlCommand cmdHandler) {
this.cmdHandler = cmdHandler;
final int initCount = session.getTargetCount();
runningCount.set(initCount);
nodeCount = initCount;
failed.set(false);
faileCount.set(0);
//recovery nodes log
ParticipantLogEntry[] participantLogEntry = new ParticipantLogEntry[initCount];
// 执行
int started = 0;
for (RouteResultsetNode rrn : session.getTargetKeys()) {
if (rrn == null) {
LOGGER.error("null is contained in RoutResultsetNodes, source = "
+ session.getSource());
continue;
}
final BackendConnection conn = session.getTarget(rrn);
if (conn != null) {
conn.setResponseHandler(this);
//process the XA_END XA_PREPARE Command
MySQLConnection mysqlCon = (MySQLConnection) conn;
String xaTxId = session.getXaTXID();
if (mysqlCon.getXaStatus() == TxState.TX_STARTED_STATE)
{
//recovery Log
participantLogEntry[started] = new ParticipantLogEntry(xaTxId,conn.getHost(),0,conn.getSchema(),((MySQLConnection) conn).getXaStatus());
String[] cmds = new String[]{"XA END " + xaTxId,
"XA PREPARE " + xaTxId};
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Start execute the batch cmd : "+ cmds[0] + ";" + cmds[1]+","+
"current connection:"+conn.getHost()+":"+conn.getPort());
}
mysqlCon.execBatchCmd(cmds);
} else
{
//recovery Log
participantLogEntry[started] = new ParticipantLogEntry(xaTxId,conn.getHost(),0,conn.getSchema(),((MySQLConnection) conn).getXaStatus());
cmdHandler.sendCommand(session, conn);
}
++started;
}
}
//xa recovery log
if(session.getXaTXID()!=null) {
CoordinatorLogEntry coordinatorLogEntry = new CoordinatorLogEntry(session.getXaTXID(), false, participantLogEntry);
inMemoryRepository.put(session.getXaTXID(), coordinatorLogEntry);
fileRepository.writeCheckpoint(inMemoryRepository.getAllCoordinatorLogEntries());
}
if (started < nodeCount) {
runningCount.set(started);
LOGGER.warn("some connection failed to execute "
+ (nodeCount - started));
/**
* assumption: only caused by front-end connection close. <br/>
* Otherwise, packet must be returned to front-end
*/
failed.set(true);
}
}
private boolean finished() {
int val = runningCount.decrementAndGet();
return (val == 0);
}
@Override
public void connectionError(Throwable e, BackendConnection conn) {
}
@Override
public void connectionAcquired(BackendConnection conn) {
}
@Override
public void errorResponse(byte[] err, BackendConnection conn) {
faileCount.incrementAndGet();
if (this.cmdHandler.releaseConOnErr()) {
session.releaseConnection(conn);
} else {
session.releaseConnectionIfSafe(conn, LOGGER.isDebugEnabled(),
false);
}
if (this.finished()) {
cmdHandler.errorResponse(session, err, this.nodeCount,
this.faileCount.get());
if (cmdHandler.isAutoClearSessionCons()) {
session.clearResources(session.getSource().isTxInterrupted());
}
}
}
@Override
public void okResponse(byte[] ok, BackendConnection conn) {
//process the XA Transatcion 2pc commit
if(conn instanceof MySQLConnection)
{
MySQLConnection mysqlCon = (MySQLConnection) conn;
switch (mysqlCon.getXaStatus())
{
case TxState.TX_STARTED_STATE:
//if there have many SQL execute wait the okResponse,will come to here one by one
//should be wait all nodes ready ,then send xa commit to all nodes.
if (mysqlCon.batchCmdFinished())
{
String xaTxId = session.getXaTXID();
String cmd = "XA COMMIT " + xaTxId;
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Start execute the cmd :"+cmd+",current host:"+
mysqlCon.getHost()+":"+mysqlCon.getPort());
}
//recovery log
CoordinatorLogEntry coordinatorLogEntry = inMemoryRepository.get(xaTxId);
for(int i=0; i<coordinatorLogEntry.participants.length;i++){
LOGGER.debug("[In Memory CoordinatorLogEntry]"+coordinatorLogEntry.participants[i]);
if(coordinatorLogEntry.participants[i].resourceName.equals(conn.getSchema())){
coordinatorLogEntry.participants[i].txState = TxState.TX_PREPARED_STATE;
}
}
inMemoryRepository.put(session.getXaTXID(),coordinatorLogEntry);
fileRepository.writeCheckpoint(inMemoryRepository.getAllCoordinatorLogEntries());
//send commit
mysqlCon.setXaStatus(TxState.TX_PREPARED_STATE);
mysqlCon.execCmd(cmd);
}
return;
case TxState.TX_PREPARED_STATE:
{
//recovery log
String xaTxId = session.getXaTXID();
CoordinatorLogEntry coordinatorLogEntry = inMemoryRepository.get(xaTxId);
for(int i=0; i<coordinatorLogEntry.participants.length;i++){
if(coordinatorLogEntry.participants[i].resourceName.equals(conn.getSchema())){
coordinatorLogEntry.participants[i].txState = TxState.TX_COMMITED_STATE;
}
}
inMemoryRepository.put(session.getXaTXID(),coordinatorLogEntry);
fileRepository.writeCheckpoint(inMemoryRepository.getAllCoordinatorLogEntries());
//XA reset status now
mysqlCon.setXaStatus(TxState.TX_INITIALIZE_STATE);
break;
}
default:
// LOGGER.error("Wrong XA status flag!");
}
}
if (this.cmdHandler.relaseConOnOK()) {
session.releaseConnection(conn);
} else {
session.releaseConnectionIfSafe(conn, LOGGER.isDebugEnabled(),
false);
}
if (this.finished()) {
cmdHandler.okResponse(session, ok);
if (cmdHandler.isAutoClearSessionCons()) {
session.clearResources(false);
}
}
}
@Override
public void fieldEofResponse(byte[] header, List<byte[]> fields,
byte[] eof, BackendConnection conn) {
}
@Override
public void rowResponse(byte[] row, BackendConnection conn) {
}
@Override
public void rowEofResponse(byte[] eof, BackendConnection conn) {
}
@Override
public void writeQueueAvailable() {
}
@Override
public void connectionClose(BackendConnection conn, String reason) {
}
}
@@ -0,0 +1,238 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat.backend.mysql.nio.handler;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;
import org.slf4j.Logger; import org.slf4j.LoggerFactory;
import io.mycat.backend.BackendConnection;
import io.mycat.config.ErrorCode;
import io.mycat.net.mysql.ErrorPacket;
import io.mycat.server.NonBlockingSession;
import io.mycat.util.StringUtil;
/**
* @author mycat
*/
abstract class MultiNodeHandler implements ResponseHandler, Terminatable {
private static final Logger LOGGER = LoggerFactory
.getLogger(MultiNodeHandler.class);
protected final ReentrantLock lock = new ReentrantLock();
protected final NonBlockingSession session;
private AtomicBoolean isFailed = new AtomicBoolean(false);
protected volatile String error;
protected byte packetId;
protected final AtomicBoolean errorRepsponsed = new AtomicBoolean(false);
public MultiNodeHandler(NonBlockingSession session) {
if (session == null) {
throw new IllegalArgumentException("session is null!");
}
this.session = session;
}
public void setFail(String errMsg) {
isFailed.set(true);
error = errMsg;
}
public boolean isFail() {
return isFailed.get();
}
private int nodeCount;
private Runnable terminateCallBack;
@Override
public void terminate(Runnable terminateCallBack) {
boolean zeroReached = false;
lock.lock();
try {
if (nodeCount > 0) {
this.terminateCallBack = terminateCallBack;
} else {
zeroReached = true;
}
} finally {
lock.unlock();
}
if (zeroReached) {
terminateCallBack.run();
}
}
protected boolean canClose(BackendConnection conn, boolean tryErrorFinish) {
// realse this connection if safe
session.releaseConnectionIfSafe(conn, LOGGER.isDebugEnabled(), false);
boolean allFinished = false;
if (tryErrorFinish) {
allFinished = this.decrementCountBy(1);
this.tryErrorFinished(allFinished);
}
return allFinished;
}
protected void decrementCountToZero() {
Runnable callback;
lock.lock();
try {
nodeCount = 0;
callback = this.terminateCallBack;
this.terminateCallBack = null;
} finally {
lock.unlock();
}
if (callback != null) {
callback.run();
}
}
public void connectionError(Throwable e, BackendConnection conn) {
final boolean canClose = decrementCountBy(1);
// 需要把Throwable e的错误信息保存下来setFail() 否则会导致响应
//null信息结果mysql命令行等客户端查询结果是"Query OK"
// @author Uncle-pan
// @since 2016-03-26
if(canClose){
setFail("backend connect: "+e);
}
LOGGER.warn("backend connect", e);
this.tryErrorFinished(canClose);
}
public void errorResponse(byte[] data, BackendConnection conn) {
session.releaseConnectionIfSafe(conn, LOGGER.isDebugEnabled(), false);
ErrorPacket err = new ErrorPacket();
err.read(data);
String errmsg = new String(err.message);
this.setFail(errmsg);
LOGGER.warn("error response from " + conn + " err " + errmsg + " code:" + err.errno);
this.tryErrorFinished(this.decrementCountBy(1));
}
public boolean clearIfSessionClosed(NonBlockingSession session) {
if (session.closed()) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("session closed ,clear resources " + session);
}
session.clearResources(true);
this.clearResources();
return true;
} else {
return false;
}
}
protected boolean decrementCountBy(int finished) {
boolean zeroReached = false;
Runnable callback = null;
lock.lock();
try {
if (zeroReached = --nodeCount == 0) {
callback = this.terminateCallBack;
this.terminateCallBack = null;
}
} finally {
lock.unlock();
}
if (zeroReached && callback != null) {
callback.run();
}
return zeroReached;
}
protected void reset(int initCount) {
nodeCount = initCount;
isFailed.set(false);
error = null;
packetId = 0;
}
protected ErrorPacket createErrPkg(String errmgs) {
ErrorPacket err = new ErrorPacket();
lock.lock();
try {
err.packetId = ++packetId;
} finally {
lock.unlock();
}
err.errno = ErrorCode.ER_UNKNOWN_ERROR;
err.message = StringUtil.encode(errmgs, session.getSource().getCharset());
return err;
}
protected void tryErrorFinished(boolean allEnd) {
if (allEnd && !session.closed()) {
if (errorRepsponsed.compareAndSet(false, true)) {
createErrPkg(this.error).write(session.getSource());
}
// clear session resources,release all
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("error all end ,clear session resource ");
}
if (session.getSource().isAutocommit()) {
session.closeAndClearResources(error);
} else {
session.getSource().setTxInterrupt(this.error);
// clear resouces
clearResources();
}
}
}
public void connectionClose(BackendConnection conn, String reason) {
this.setFail("closed connection:" + reason + " con:" + conn);
boolean finished = false;
lock.lock();
try {
finished = (this.nodeCount == 0);
} finally {
lock.unlock();
}
if (finished == false) {
finished = this.decrementCountBy(1);
}
if (error == null) {
error = "back connection closed ";
}
tryErrorFinished(finished);
}
public void clearResources() {
}
}
@@ -0,0 +1,752 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat.backend.mysql.nio.handler;
import io.mycat.memory.unsafe.row.UnsafeRow;
import io.mycat.sqlengine.mpp.*;
import org.slf4j.Logger; import org.slf4j.LoggerFactory;
import io.mycat.MycatServer;
import io.mycat.backend.BackendConnection;
import io.mycat.backend.datasource.PhysicalDBNode;
import io.mycat.backend.mysql.LoadDataUtil;
import io.mycat.cache.LayerCachePool;
import io.mycat.config.MycatConfig;
import io.mycat.net.mysql.*;
import io.mycat.route.RouteResultset;
import io.mycat.route.RouteResultsetNode;
import io.mycat.server.NonBlockingSession;
import io.mycat.server.ServerConnection;
import io.mycat.server.parser.ServerParse;
import io.mycat.statistic.stat.QueryResult;
import io.mycat.statistic.stat.QueryResultDispatcher;
import java.io.*;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author mycat
*/
public class MultiNodeQueryHandler extends MultiNodeHandler implements LoadDataResponseHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(MultiNodeQueryHandler.class);
private final RouteResultset rrs;
private final NonBlockingSession session;
// private final CommitNodeHandler icHandler;
private final AbstractDataNodeMerge dataMergeSvr;
private final boolean autocommit;
private String priamaryKeyTable = null;
private int primaryKeyIndex = -1;
private int fieldCount = 0;
private final ReentrantLock lock;
private long affectedRows;
private long selectRows;
private long insertId;
private volatile boolean fieldsReturned;
private int okCount;
private final boolean isCallProcedure;
private long startTime;
private long netInBytes;
private long netOutBytes;
private int execCount = 0;
private boolean prepared;
private List<FieldPacket> fieldPackets = new ArrayList<FieldPacket>();
private int isOffHeapuseOffHeapForMerge = 1;
/**
* Limit NM
*/
private int limitStart;
private int limitSize;
private int index = 0;
private int end = 0;
public MultiNodeQueryHandler(int sqlType, RouteResultset rrs,
boolean autocommit, NonBlockingSession session) {
super(session);
if (rrs.getNodes() == null) {
throw new IllegalArgumentException("routeNode is null!");
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("execute mutinode query " + rrs.getStatement());
}
this.rrs = rrs;
isOffHeapuseOffHeapForMerge = MycatServer.getInstance().
getConfig().getSystem().getUseOffHeapForMerge();
if (ServerParse.SELECT == sqlType && rrs.needMerge()) {
/**
* 使用Off Heap
*/
if(isOffHeapuseOffHeapForMerge == 1){
dataMergeSvr = new DataNodeMergeManager(this,rrs);
}else {
dataMergeSvr = new DataMergeService(this,rrs);
}
} else {
dataMergeSvr = null;
}
isCallProcedure = rrs.isCallStatement();
this.autocommit = session.getSource().isAutocommit();
this.session = session;
this.lock = new ReentrantLock();
// this.icHandler = new CommitNodeHandler(session);
this.limitStart = rrs.getLimitStart();
this.limitSize = rrs.getLimitSize();
this.end = limitStart + rrs.getLimitSize();
if (this.limitStart < 0)
this.limitStart = 0;
if (rrs.getLimitSize() < 0)
end = Integer.MAX_VALUE;
if ((dataMergeSvr != null)
&& LOGGER.isDebugEnabled()) {
LOGGER.debug("has data merge logic ");
}
if ( rrs != null && rrs.getStatement() != null) {
netInBytes += rrs.getStatement().getBytes().length;
}
}
protected void reset(int initCount) {
super.reset(initCount);
this.okCount = initCount;
this.execCount = 0;
this.netInBytes = 0;
this.netOutBytes = 0;
}
public NonBlockingSession getSession() {
return session;
}
public void execute() throws Exception {
final ReentrantLock lock = this.lock;
lock.lock();
try {
this.reset(rrs.getNodes().length);
this.fieldsReturned = false;
this.affectedRows = 0L;
this.insertId = 0L;
} finally {
lock.unlock();
}
MycatConfig conf = MycatServer.getInstance().getConfig();
startTime = System.currentTimeMillis();
LOGGER.debug("rrs.getRunOnSlave()-" + rrs.getRunOnSlave());
for (final RouteResultsetNode node : rrs.getNodes()) {
BackendConnection conn = session.getTarget(node);
if (session.tryExistsCon(conn, node)) {
LOGGER.debug("node.getRunOnSlave()-" + node.getRunOnSlave());
node.setRunOnSlave(rrs.getRunOnSlave()); // 实现 master/slave注解
LOGGER.debug("node.getRunOnSlave()-" + node.getRunOnSlave());
_execute(conn, node);
} else {
// create new connection
LOGGER.debug("node.getRunOnSlave()1-" + node.getRunOnSlave());
node.setRunOnSlave(rrs.getRunOnSlave()); // 实现 master/slave注解
LOGGER.debug("node.getRunOnSlave()2-" + node.getRunOnSlave());
PhysicalDBNode dn = conf.getDataNodes().get(node.getName());
dn.getConnection(dn.getDatabase(), autocommit, node, this, node);
// 注意该方法不仅仅是获取连接获取新连接成功之后会通过层层回调最后回调到本类 的connectionAcquired
// 这是通过 上面方法的 this 参数的层层传递完成的
// connectionAcquired 进行执行操作:
// session.bindConnection(node, conn);
// _execute(conn, node);
}
}
}
private void _execute(BackendConnection conn, RouteResultsetNode node) {
if (clearIfSessionClosed(session)) {
return;
}
conn.setResponseHandler(this);
try {
conn.execute(node, session.getSource(), autocommit);
} catch (IOException e) {
connectionError(e, conn);
}
}
@Override
public void connectionAcquired(final BackendConnection conn) {
final RouteResultsetNode node = (RouteResultsetNode) conn
.getAttachment();
session.bindConnection(node, conn);
_execute(conn, node);
}
private boolean decrementOkCountBy(int finished) {
lock.lock();
try {
return --okCount == 0;
} finally {
lock.unlock();
}
}
@Override
public void okResponse(byte[] data, BackendConnection conn) {
this.netOutBytes += data.length;
boolean executeResponse = conn.syncAndExcute();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("received ok response ,executeResponse:"
+ executeResponse + " from " + conn);
}
if (executeResponse) {
ServerConnection source = session.getSource();
OkPacket ok = new OkPacket();
ok.read(data);
//存储过程
boolean isCanClose2Client =(!rrs.isCallStatement()) ||(rrs.isCallStatement() &&!rrs.getProcedure().isResultSimpleValue());;
if(!isCallProcedure)
{
if (clearIfSessionClosed(session))
{
return;
} else if (canClose(conn, false))
{
return;
}
}
lock.lock();
try {
// 判断是否是全局表如果是执行行数不做累加以最后一次执行的为准
if (!rrs.isGlobalTable()) {
affectedRows += ok.affectedRows;
} else {
affectedRows = ok.affectedRows;
}
if (ok.insertId > 0) {
insertId = (insertId == 0) ? ok.insertId : Math.min(
insertId, ok.insertId);
}
} finally {
lock.unlock();
}
// 对于存储过程其比较特殊查询结果返回EndRow报文以后还会再返回一个OK报文才算结束
boolean isEndPacket = isCallProcedure ? decrementOkCountBy(1): decrementCountBy(1);
if (isEndPacket && isCanClose2Client) {
if (this.autocommit && !session.getSource().isLocked()) {// clear all connections
session.releaseConnections(false);
}
if (this.isFail() || session.closed()) {
tryErrorFinished(true);
return;
}
lock.lock();
try {
if (rrs.isLoadData()) {
byte lastPackId = source.getLoadDataInfileHandler()
.getLastPackId();
ok.packetId = ++lastPackId;// OK_PACKET
ok.message = ("Records: " + affectedRows + " Deleted: 0 Skipped: 0 Warnings: 0")
.getBytes();// 此处信息只是为了控制台给人看的
source.getLoadDataInfileHandler().clear();
} else {
ok.packetId = ++packetId;// OK_PACKET
}
ok.affectedRows = affectedRows;
ok.serverStatus = source.isAutocommit() ? 2 : 1;
if (insertId > 0) {
ok.insertId = insertId;
source.setLastInsertId(insertId);
}
ok.write(source);
} catch (Exception e) {
handleDataProcessException(e);
} finally {
lock.unlock();
}
}
}
}
@Override
public void rowEofResponse(final byte[] eof, BackendConnection conn) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("on row end reseponse " + conn);
}
this.netOutBytes += eof.length;
if (errorRepsponsed.get()) {
// the connection has been closed or set to "txInterrupt" properly
//in tryErrorFinished() method! If we close it here, it can
// lead to tx error such as blocking rollback tx for ever.
// @author Uncle-pan
// @since 2016-03-25
// conn.close(this.error);
return;
}
final ServerConnection source = session.getSource();
if (!isCallProcedure) {
if (clearIfSessionClosed(session)) {
return;
} else if (canClose(conn, false)) {
return;
}
}
if (decrementCountBy(1)) {
if (!rrs.isCallStatement()||(rrs.isCallStatement()&&rrs.getProcedure().isResultSimpleValue())) {
if (this.autocommit && !session.getSource().isLocked()) {// clear all connections
session.releaseConnections(false);
}
if (this.isFail() || session.closed()) {
tryErrorFinished(true);
return;
}
}
if (dataMergeSvr != null) {
try {
dataMergeSvr.outputMergeResult(session, eof);
} catch (Exception e) {
handleDataProcessException(e);
}
} else {
try {
lock.lock();
eof[3] = ++packetId;
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("last packet id:" + packetId);
}
source.write(eof);
} finally {
lock.unlock();
}
}
}
execCount++;
if (execCount == rrs.getNodes().length) {
int resultSize = source.getWriteQueue().size()*MycatServer.getInstance().getConfig().getSystem().getBufferPoolPageSize();
//TODO: add by zhuam
//查询结果派发
QueryResult queryResult = new QueryResult(session.getSource().getUser(),
rrs.getSqlType(), rrs.getStatement(), selectRows, netInBytes, netOutBytes, startTime, System.currentTimeMillis(),resultSize);
QueryResultDispatcher.dispatchQuery( queryResult );
}
}
/**
* 将汇聚结果集数据真正的发送给Mycat客户端
* @param source
* @param eof
* @param
*/
public void outputMergeResult(final ServerConnection source, final byte[] eof, Iterator<UnsafeRow> iter) {
try {
lock.lock();
ByteBuffer buffer = session.getSource().allocate();
final RouteResultset rrs = this.dataMergeSvr.getRrs();
/**
* 处理limit语句的start end位置将正确的结果发送给
* Mycat 客户端
*/
int start = rrs.getLimitStart();
int end = start + rrs.getLimitSize();
int index = 0;
if (start < 0)
start = 0;
if (rrs.getLimitSize() < 0)
end = Integer.MAX_VALUE;
if(prepared) {
while (iter.hasNext()){
UnsafeRow row = iter.next();
if(index >= start){
row.packetId = ++packetId;
BinaryRowDataPacket binRowPacket = new BinaryRowDataPacket();
binRowPacket.read(fieldPackets, row);
buffer = binRowPacket.write(buffer, source, true);
}
index++;
if(index == end){
break;
}
}
} else {
while (iter.hasNext()){
UnsafeRow row = iter.next();
if(index >= start){
row.packetId = ++packetId;
buffer = row.write(buffer,source,true);
}
index++;
if(index == end){
break;
}
}
}
eof[3] = ++packetId;
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("last packet id:" + packetId);
}
/**
* 真正的开始把Writer Buffer的数据写入到channel
*/
source.write(source.writeToBuffer(eof, buffer));
} catch (Exception e) {
handleDataProcessException(e);
} finally {
lock.unlock();
dataMergeSvr.clear();
}
}
public void outputMergeResult(final ServerConnection source,
final byte[] eof, List<RowDataPacket> results) {
try {
lock.lock();
ByteBuffer buffer = session.getSource().allocate();
final RouteResultset rrs = this.dataMergeSvr.getRrs();
// 处理limit语句
int start = rrs.getLimitStart();
int end = start + rrs.getLimitSize();
if (start < 0) {
start = 0;
}
if (rrs.getLimitSize() < 0) {
end = results.size();
}
// // 对于不需要排序的语句,返回的数据只有rrs.getLimitSize()
// if (rrs.getOrderByCols() == null) {
// end = results.size();
// start = 0;
// }
if (end > results.size()) {
end = results.size();
}
// for (int i = start; i < end; i++) {
// RowDataPacket row = results.get(i);
// if( prepared ) {
// BinaryRowDataPacket binRowDataPk = new BinaryRowDataPacket();
// binRowDataPk.read(fieldPackets, row);
// binRowDataPk.packetId = ++packetId;
// //binRowDataPk.write(source);
// buffer = binRowDataPk.write(buffer, session.getSource(), true);
// } else {
// row.packetId = ++packetId;
// buffer = row.write(buffer, source, true);
// }
// }
if(prepared) {
for (int i = start; i < end; i++) {
RowDataPacket row = results.get(i);
BinaryRowDataPacket binRowDataPk = new BinaryRowDataPacket();
binRowDataPk.read(fieldPackets, row);
binRowDataPk.packetId = ++packetId;
//binRowDataPk.write(source);
buffer = binRowDataPk.write(buffer, session.getSource(), true);
}
} else {
for (int i = start; i < end; i++) {
RowDataPacket row = results.get(i);
row.packetId = ++packetId;
buffer = row.write(buffer, source, true);
}
}
eof[3] = ++packetId;
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("last packet id:" + packetId);
}
source.write(source.writeToBuffer(eof, buffer));
} catch (Exception e) {
handleDataProcessException(e);
} finally {
lock.unlock();
dataMergeSvr.clear();
}
}
@Override
public void fieldEofResponse(byte[] header, List<byte[]> fields,
byte[] eof, BackendConnection conn) {
this.netOutBytes += header.length;
this.netOutBytes += eof.length;
for (int i = 0, len = fields.size(); i < len; ++i) {
byte[] field = fields.get(i);
this.netOutBytes += field.length;
}
ServerConnection source = null;
if (fieldsReturned) {
return;
}
lock.lock();
try {
if (fieldsReturned) {
return;
}
fieldsReturned = true;
boolean needMerg = (dataMergeSvr != null)
&& dataMergeSvr.getRrs().needMerge();
Set<String> shouldRemoveAvgField = new HashSet<>();
Set<String> shouldRenameAvgField = new HashSet<>();
if (needMerg) {
Map<String, Integer> mergeColsMap = dataMergeSvr.getRrs()
.getMergeCols();
if (mergeColsMap != null) {
for (Map.Entry<String, Integer> entry : mergeColsMap
.entrySet()) {
String key = entry.getKey();
int mergeType = entry.getValue();
if (MergeCol.MERGE_AVG == mergeType
&& mergeColsMap.containsKey(key + "SUM")) {
shouldRemoveAvgField.add((key + "COUNT")
.toUpperCase());
shouldRenameAvgField.add((key + "SUM")
.toUpperCase());
}
}
}
}
source = session.getSource();
ByteBuffer buffer = source.allocate();
fieldCount = fields.size();
if (shouldRemoveAvgField.size() > 0) {
ResultSetHeaderPacket packet = new ResultSetHeaderPacket();
packet.packetId = ++packetId;
packet.fieldCount = fieldCount - shouldRemoveAvgField.size();
buffer = packet.write(buffer, source, true);
} else {
header[3] = ++packetId;
buffer = source.writeToBuffer(header, buffer);
}
String primaryKey = null;
if (rrs.hasPrimaryKeyToCache()) {
String[] items = rrs.getPrimaryKeyItems();
priamaryKeyTable = items[0];
primaryKey = items[1];
}
Map<String, ColMeta> columToIndx = new HashMap<String, ColMeta>(
fieldCount);
for (int i = 0, len = fieldCount; i < len; ++i) {
boolean shouldSkip = false;
byte[] field = fields.get(i);
if (needMerg) {
FieldPacket fieldPkg = new FieldPacket();
fieldPkg.read(field);
fieldPackets.add(fieldPkg);
String fieldName = new String(fieldPkg.name).toUpperCase();
if (columToIndx != null
&& !columToIndx.containsKey(fieldName)) {
if (shouldRemoveAvgField.contains(fieldName)) {
shouldSkip = true;
fieldPackets.remove(fieldPackets.size() - 1);
}
if (shouldRenameAvgField.contains(fieldName)) {
String newFieldName = fieldName.substring(0,
fieldName.length() - 3);
fieldPkg.name = newFieldName.getBytes();
fieldPkg.packetId = ++packetId;
shouldSkip = true;
// 处理AVG字段位数和精度, AVG位数 = SUM位数 - 14
fieldPkg.length = fieldPkg.length - 14;
// AVG精度 = SUM精度 + 4
fieldPkg.decimals = (byte) (fieldPkg.decimals + 4);
buffer = fieldPkg.write(buffer, source, false);
// 还原精度
fieldPkg.decimals = (byte) (fieldPkg.decimals - 4);
}
ColMeta colMeta = new ColMeta(i, fieldPkg.type);
colMeta.decimals = fieldPkg.decimals;
columToIndx.put(fieldName, colMeta);
}
} else {
FieldPacket fieldPkg = new FieldPacket();
fieldPkg.read(field);
fieldPackets.add(fieldPkg);
fieldCount = fields.size();
if (primaryKey != null && primaryKeyIndex == -1) {
// find primary key index
String fieldName = new String(fieldPkg.name);
if (primaryKey.equalsIgnoreCase(fieldName)) {
primaryKeyIndex = i;
}
} }
if (!shouldSkip) {
field[3] = ++packetId;
buffer = source.writeToBuffer(field, buffer);
}
}
eof[3] = ++packetId;
buffer = source.writeToBuffer(eof, buffer);
source.write(buffer);
if (dataMergeSvr != null) {
dataMergeSvr.onRowMetaData(columToIndx, fieldCount);
}
} catch (Exception e) {
handleDataProcessException(e);
} finally {
lock.unlock();
}
}
public void handleDataProcessException(Exception e) {
if (!errorRepsponsed.get()) {
this.error = e.toString();
LOGGER.warn("caught exception ", e);
setFail(e.toString());
this.tryErrorFinished(true);
}
}
@Override
public void rowResponse(final byte[] row, final BackendConnection conn) {
if (errorRepsponsed.get()) {
// the connection has been closed or set to "txInterrupt" properly
//in tryErrorFinished() method! If we close it here, it can
// lead to tx error such as blocking rollback tx for ever.
// @author Uncle-pan
// @since 2016-03-25
//conn.close(error);
return;
}
lock.lock();
try {
this.selectRows++;
RouteResultsetNode rNode = (RouteResultsetNode) conn.getAttachment();
String dataNode = rNode.getName();
if (dataMergeSvr != null) {
// even through discarding the all rest data, we can't
//close the connection for tx control such as rollback or commit.
// So the "isClosedByDiscard" variable is unnecessary.
// @author Uncle-pan
// @since 2016-03-25
dataMergeSvr.onNewRecord(dataNode, row);
} else {
RowDataPacket rowDataPkg =null;
// cache primaryKey-> dataNode
if (primaryKeyIndex != -1) {
rowDataPkg = new RowDataPacket(fieldCount);
rowDataPkg.read(row);
String primaryKey = new String(rowDataPkg.fieldValues.get(primaryKeyIndex));
LayerCachePool pool = MycatServer.getInstance().getRouterservice().getTableId2DataNodeCache();
pool.putIfAbsent(priamaryKeyTable, primaryKey, dataNode);
}
row[3] = ++packetId;
if( prepared ) {
if(rowDataPkg==null) {
rowDataPkg = new RowDataPacket(fieldCount);
rowDataPkg.read(row);
}
BinaryRowDataPacket binRowDataPk = new BinaryRowDataPacket();
binRowDataPk.read(fieldPackets, rowDataPkg);
binRowDataPk.write(session.getSource());
} else {
session.getSource().write(row);
}
}
} catch (Exception e) {
handleDataProcessException(e);
} finally {
lock.unlock();
}
}
@Override
public void clearResources() {
if (dataMergeSvr != null) {
dataMergeSvr.clear();
}
}
@Override
public void writeQueueAvailable() {
}
@Override
public void requestDataResponse(byte[] data, BackendConnection conn) {
LoadDataUtil.requestFileDataResponse(data, conn);
}
public boolean isPrepared() {
return prepared;
}
public void setPrepared(boolean prepared) {
this.prepared = prepared;
}
}
@@ -0,0 +1,92 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat.backend.mysql.nio.handler;
import java.util.List;
import org.slf4j.Logger; import org.slf4j.LoggerFactory;
import io.mycat.backend.BackendConnection;
public class NewConnectionRespHandler implements ResponseHandler{
private static final Logger LOGGER = LoggerFactory
.getLogger(NewConnectionRespHandler.class);
@Override
public void connectionError(Throwable e, BackendConnection conn) {
LOGGER.warn(conn+" connectionError "+e);
}
@Override
public void connectionAcquired(BackendConnection conn) {
//
conn.release();
LOGGER.info("connectionAcquired "+conn);
}
@Override
public void errorResponse(byte[] err, BackendConnection conn) {
LOGGER.warn("caught error resp: " + conn + " " + new String(err));
}
@Override
public void okResponse(byte[] ok, BackendConnection conn) {
LOGGER.info("okResponse: " + conn );
}
@Override
public void fieldEofResponse(byte[] header, List<byte[]> fields,
byte[] eof, BackendConnection conn) {
LOGGER.info("fieldEofResponse: " + conn );
}
@Override
public void rowResponse(byte[] row, BackendConnection conn) {
LOGGER.info("rowResponse: " + conn );
}
@Override
public void rowEofResponse(byte[] eof, BackendConnection conn) {
LOGGER.info("rowEofResponse: " + conn );
}
@Override
public void writeQueueAvailable() {
}
@Override
public void connectionClose(BackendConnection conn, String reason) {
}
}
@@ -0,0 +1,87 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat.backend.mysql.nio.handler;
import java.util.List;
import io.mycat.backend.BackendConnection;
/**
* @author mycat
* @author mycat
*/
public interface ResponseHandler {
/**
* 无法获取连接
*
* @param e
* @param conn
*/
public void connectionError(Throwable e, BackendConnection conn);
/**
* 已获得有效连接的响应处理
*/
void connectionAcquired(BackendConnection conn);
/**
* 收到错误数据包的响应处理
*/
void errorResponse(byte[] err, BackendConnection conn);
/**
* 收到OK数据包的响应处理
*/
void okResponse(byte[] ok, BackendConnection conn);
/**
* 收到字段数据包结束的响应处理
*/
void fieldEofResponse(byte[] header, List<byte[]> fields, byte[] eof,
BackendConnection conn);
/**
* 收到行数据包的响应处理
*/
void rowResponse(byte[] row, BackendConnection conn);
/**
* 收到行数据包结束的响应处理
*/
void rowEofResponse(byte[] eof, BackendConnection conn);
/**
* 写队列为空可以写数据了
*
*/
void writeQueueAvailable();
/**
* on connetion close event
*/
void connectionClose(BackendConnection conn, String reason);
}
@@ -0,0 +1,156 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat.backend.mysql.nio.handler;
import java.util.List;
import io.mycat.backend.mysql.nio.MySQLConnection;
import io.mycat.config.ErrorCode;
import org.slf4j.Logger; import org.slf4j.LoggerFactory;
import io.mycat.backend.BackendConnection;
import io.mycat.route.RouteResultsetNode;
import io.mycat.server.NonBlockingSession;
/**
* @author mycat
*/
public class RollbackNodeHandler extends MultiNodeHandler {
private static final Logger LOGGER = LoggerFactory
.getLogger(RollbackNodeHandler.class);
public RollbackNodeHandler(NonBlockingSession session) {
super(session);
}
public void rollback() {
final int initCount = session.getTargetCount();
lock.lock();
try {
reset(initCount);
} finally {
lock.unlock();
}
if (session.closed()) {
decrementCountToZero();
return;
}
// 执行
int started = 0;
for (final RouteResultsetNode node : session.getTargetKeys()) {
if (node == null) {
LOGGER.error("null is contained in RoutResultsetNodes, source = "
+ session.getSource());
continue;
}
final BackendConnection conn = session.getTarget(node);
if (conn != null) {
boolean isClosed=conn.isClosedOrQuit();
if(isClosed)
{
session.getSource().writeErrMessage(ErrorCode.ER_UNKNOWN_ERROR,
"receive rollback,but find backend con is closed or quit");
LOGGER.error( conn+"receive rollback,but fond backend con is closed or quit");
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("rollback job run for " + conn);
}
if (clearIfSessionClosed(session)) {
return;
}
conn.setResponseHandler(RollbackNodeHandler.this);
//support the XA rollback
MySQLConnection mysqlCon = (MySQLConnection) conn;
if(session.getXaTXID()!=null) {
String xaTxId = session.getXaTXID();
//exeBatch cmd issue : the 2nd package can not receive the response
mysqlCon.execCmd("XA END " + xaTxId + ";");
mysqlCon.execCmd("XA ROLLBACK " + xaTxId + ";");
}else {
conn.rollback();
}
++started;
}
}
if (started < initCount && decrementCountBy(initCount - started)) {
/**
* assumption: only caused by front-end connection close. <br/>
* Otherwise, packet must be returned to front-end
*/
session.clearResources(true);
}
}
@Override
public void okResponse(byte[] ok, BackendConnection conn) {
if (decrementCountBy(1)) {
// clear all resources
session.clearResources(false);
if (this.isFail() || session.closed()) {
tryErrorFinished(true);
} else {
session.getSource().write(ok);
}
}
}
@Override
public void rowEofResponse(byte[] eof, BackendConnection conn) {
LOGGER.error(new StringBuilder().append("unexpected packet for ")
.append(conn).append(" bound by ").append(session.getSource())
.append(": field's eof").toString());
}
@Override
public void connectionAcquired(BackendConnection conn) {
LOGGER.error("unexpected invocation: connectionAcquired from rollback");
}
@Override
public void fieldEofResponse(byte[] header, List<byte[]> fields,
byte[] eof, BackendConnection conn) {
LOGGER.error(new StringBuilder().append("unexpected packet for ")
.append(conn).append(" bound by ").append(session.getSource())
.append(": field's eof").toString());
}
@Override
public void rowResponse(byte[] row, BackendConnection conn) {
LOGGER.error(new StringBuilder().append("unexpected packet for ")
.append(conn).append(" bound by ").append(session.getSource())
.append(": field's eof").toString());
}
@Override
public void writeQueueAvailable() {
}
}
@@ -0,0 +1,87 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat.backend.mysql.nio.handler;
import java.util.List;
import org.slf4j.Logger; import org.slf4j.LoggerFactory;
import io.mycat.backend.BackendConnection;
/**
* @author mycat
*/
public class RollbackReleaseHandler implements ResponseHandler {
private static final Logger logger = LoggerFactory
.getLogger(RollbackReleaseHandler.class);
public RollbackReleaseHandler() {
}
@Override
public void connectionAcquired(BackendConnection conn) {
logger.error("unexpected invocation: connectionAcquired from rollback-release");
}
@Override
public void connectionError(Throwable e, BackendConnection conn) {
}
@Override
public void errorResponse(byte[] err, BackendConnection conn) {
conn.quit();
}
@Override
public void okResponse(byte[] ok, BackendConnection conn) {
logger.debug("autocomit is false,but no commit or rollback ,so mycat rollbacked backend conn "+conn);
conn.release();
}
@Override
public void fieldEofResponse(byte[] header, List<byte[]> fields,
byte[] eof, BackendConnection conn) {
}
@Override
public void rowResponse(byte[] row, BackendConnection conn) {
}
@Override
public void rowEofResponse(byte[] eof, BackendConnection conn) {
}
@Override
public void writeQueueAvailable() {
}
@Override
public void connectionClose(BackendConnection conn, String reason) {
}
}
@@ -0,0 +1,89 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat.backend.mysql.nio.handler;
import java.util.List;
import org.slf4j.Logger; import org.slf4j.LoggerFactory;
import io.mycat.backend.BackendConnection;
public class SimpleLogHandler implements ResponseHandler{
private static final Logger LOGGER = LoggerFactory
.getLogger(SimpleLogHandler.class);
@Override
public void connectionError(Throwable e, BackendConnection conn) {
LOGGER.warn(conn+" connectionError "+e);
}
@Override
public void connectionAcquired(BackendConnection conn) {
LOGGER.info("connectionAcquired "+conn);
}
@Override
public void errorResponse(byte[] err, BackendConnection conn) {
LOGGER.warn("caught error resp: " + conn + " " + new String(err));
}
@Override
public void okResponse(byte[] ok, BackendConnection conn) {
LOGGER.info("okResponse: " + conn );
}
@Override
public void fieldEofResponse(byte[] header, List<byte[]> fields,
byte[] eof, BackendConnection conn) {
LOGGER.info("fieldEofResponse: " + conn );
}
@Override
public void rowResponse(byte[] row, BackendConnection conn) {
LOGGER.info("rowResponse: " + conn );
}
@Override
public void rowEofResponse(byte[] eof, BackendConnection conn) {
LOGGER.info("rowEofResponse: " + conn );
}
@Override
public void writeQueueAvailable() {
}
@Override
public void connectionClose(BackendConnection conn, String reason) {
}
}
@@ -0,0 +1,501 @@
/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package io.mycat.backend.mysql.nio.handler;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import com.google.common.base.Strings;
import io.mycat.MycatServer;
import io.mycat.backend.BackendConnection;
import io.mycat.backend.datasource.PhysicalDBNode;
import io.mycat.backend.mysql.LoadDataUtil;
import io.mycat.config.ErrorCode;
import io.mycat.config.MycatConfig;
import io.mycat.config.model.SchemaConfig;
import io.mycat.net.mysql.BinaryRowDataPacket;
import io.mycat.net.mysql.ErrorPacket;
import io.mycat.net.mysql.FieldPacket;
import io.mycat.net.mysql.OkPacket;
import io.mycat.net.mysql.RowDataPacket;
import io.mycat.route.RouteResultset;
import io.mycat.route.RouteResultsetNode;
import io.mycat.server.NonBlockingSession;
import io.mycat.server.ServerConnection;
import io.mycat.server.parser.ServerParse;
import io.mycat.server.parser.ServerParseShow;
import io.mycat.server.response.ShowFullTables;
import io.mycat.server.response.ShowTables;
import io.mycat.statistic.stat.QueryResult;
import io.mycat.statistic.stat.QueryResultDispatcher;
import io.mycat.util.StringUtil;
import org.slf4j.Logger; import org.slf4j.LoggerFactory;
/**
* @author mycat
*/
public class SingleNodeHandler implements ResponseHandler, Terminatable, LoadDataResponseHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(SingleNodeHandler.class);
private final RouteResultsetNode node;
private final RouteResultset rrs;
private final NonBlockingSession session;
// only one thread access at one time no need lock
private volatile byte packetId;
private volatile ByteBuffer buffer;
private volatile boolean isRunning;
private Runnable terminateCallBack;
private long startTime;
private long netInBytes;
private long netOutBytes;
private long selectRows;
private long affectedRows;
private boolean prepared;
private int fieldCount;
private List<FieldPacket> fieldPackets = new ArrayList<FieldPacket>();
private volatile boolean isDefaultNodeShowTable;
private volatile boolean isDefaultNodeShowFullTable;
private Set<String> shardingTablesSet;
public SingleNodeHandler(RouteResultset rrs, NonBlockingSession session) {
this.rrs = rrs;
this.node = rrs.getNodes()[0];
if (node == null) {
throw new IllegalArgumentException("routeNode is null!");
}
if (session == null) {
throw new IllegalArgumentException("session is null!");
}
this.session = session;
ServerConnection source = session.getSource();
String schema = source.getSchema();
if (schema != null && ServerParse.SHOW == rrs.getSqlType()) {
SchemaConfig schemaConfig = MycatServer.getInstance().getConfig().getSchemas().get(schema);
int type = ServerParseShow.tableCheck(rrs.getStatement(), 0);
isDefaultNodeShowTable = (ServerParseShow.TABLES == type && !Strings.isNullOrEmpty(schemaConfig.getDataNode()));
isDefaultNodeShowFullTable = (ServerParseShow.FULLTABLES == type && !Strings.isNullOrEmpty(schemaConfig.getDataNode()));
if (isDefaultNodeShowTable) {
shardingTablesSet = ShowTables.getTableSet(source, rrs.getStatement());
} else if (isDefaultNodeShowFullTable) {
shardingTablesSet = ShowFullTables.getTableSet(source, rrs.getStatement());
}
}
if ( rrs != null && rrs.getStatement() != null) {
netInBytes += rrs.getStatement().getBytes().length;
}
}
@Override
public void terminate(Runnable callback) {
boolean zeroReached = false;
if (isRunning) {
terminateCallBack = callback;
} else {
zeroReached = true;
}
if (zeroReached) {
callback.run();
}
}
private void endRunning() {
Runnable callback = null;
if (isRunning) {
isRunning = false;
callback = terminateCallBack;
terminateCallBack = null;
}
if (callback != null) {
callback.run();
}
}
private void recycleResources() {
ByteBuffer buf = buffer;
if (buf != null) {
session.getSource().recycle(buffer);
buffer = null;
}
}
public void execute() throws Exception {
startTime=System.currentTimeMillis();
ServerConnection sc = session.getSource();
this.isRunning = true;
this.packetId = 0;
final BackendConnection conn = session.getTarget(node);
LOGGER.debug("rrs.getRunOnSlave() " + rrs.getRunOnSlave());
node.setRunOnSlave(rrs.getRunOnSlave()); // 实现 master/slave注解
LOGGER.debug("node.getRunOnSlave() " + node.getRunOnSlave());
if (session.tryExistsCon(conn, node)) {
_execute(conn);
} else {
// create new connection
MycatConfig conf = MycatServer.getInstance().getConfig();
LOGGER.debug("node.getRunOnSlave() " + node.getRunOnSlave());
node.setRunOnSlave(rrs.getRunOnSlave()); // 实现 master/slave注解
LOGGER.debug("node.getRunOnSlave() " + node.getRunOnSlave());
PhysicalDBNode dn = conf.getDataNodes().get(node.getName());
dn.getConnection(dn.getDatabase(), sc.isAutocommit(), node, this, node);
}
}
@Override
public void connectionAcquired(final BackendConnection conn) {
session.bindConnection(node, conn);
_execute(conn);
}
private void _execute(BackendConnection conn) {
if (session.closed()) {
endRunning();
session.clearResources(true);
return;
}
conn.setResponseHandler(this);
try {
conn.execute(node, session.getSource(), session.getSource()
.isAutocommit());
} catch (Exception e1) {
executeException(conn, e1);
return;
}
}
private void executeException(BackendConnection c, Exception e) {
ErrorPacket err = new ErrorPacket();
err.packetId = ++packetId;
err.errno = ErrorCode.ERR_FOUND_EXCEPION;
err.message = StringUtil.encode(e.toString(), session.getSource().getCharset());
this.backConnectionErr(err, c);
}
@Override
public void connectionError(Throwable e, BackendConnection conn) {
endRunning();
ErrorPacket err = new ErrorPacket();
err.packetId = ++packetId;
err.errno = ErrorCode.ER_NEW_ABORTING_CONNECTION;
err.message = StringUtil.encode(e.getMessage(), session.getSource().getCharset());
ServerConnection source = session.getSource();
source.write(err.write(allocBuffer(), source, true));
}
@Override
public void errorResponse(byte[] data, BackendConnection conn) {
ErrorPacket err = new ErrorPacket();
err.read(data);
err.packetId = ++packetId;
backConnectionErr(err, conn);
}
private void backConnectionErr(ErrorPacket errPkg, BackendConnection conn) {
endRunning();
ServerConnection source = session.getSource();
String errUser = source.getUser();
String errHost = source.getHost();
int errPort = source.getLocalPort();
String errmgs = " errno:" + errPkg.errno + " " + new String(errPkg.message);
LOGGER.warn("execute sql err :" + errmgs + " con:" + conn
+ " frontend host:" + errHost + "/" + errPort + "/" + errUser);
session.releaseConnectionIfSafe(conn, LOGGER.isDebugEnabled(), false);
source.setTxInterrupt(errmgs);
/**
* TODO: 修复全版本BUG
*
* BUG复现
* 1MysqlClient: SELECT 9223372036854775807 + 1;
* 2MyCatServer: ERROR 1690 (22003): BIGINT value is out of range in '(9223372036854775807 + 1)'
* 3MysqlClient: ERROR 2013 (HY000): Lost connection to MySQL server during query
*
* Fixed后
* 1MysqlClient: SELECT 9223372036854775807 + 1;
* 2MyCatServer: ERROR 1690 (22003): BIGINT value is out of range in '(9223372036854775807 + 1)'
* 3MysqlClient: ERROR 1690 (22003): BIGINT value is out of range in '(9223372036854775807 + 1)'
*
*/
// 由于 pakcetId != 1 造成的问题
errPkg.packetId = 1;
errPkg.write(source);
recycleResources();
}
/**
* insert/update/delete
*
* okResponse()读取data字节数组组成一个OKPacket并调用ok.write(source)将结果写入前端连接FrontendConnection的写缓冲队列writeQueue中
* 真正发送给应用是由对应的NIOSocketWR从写队列中读取ByteBuffer并返回的
*/
@Override
public void okResponse(byte[] data, BackendConnection conn) {
//
this.netOutBytes += data.length;
boolean executeResponse = conn.syncAndExcute();
if (executeResponse) {
ServerConnection source = session.getSource();
OkPacket ok = new OkPacket();
ok.read(data);
boolean isCanClose2Client =(!rrs.isCallStatement()) ||(rrs.isCallStatement() &&!rrs.getProcedure().isResultSimpleValue());
if (rrs.isLoadData()) {
byte lastPackId = source.getLoadDataInfileHandler().getLastPackId();
ok.packetId = ++lastPackId;// OK_PACKET
source.getLoadDataInfileHandler().clear();
} else if (isCanClose2Client) {
ok.packetId = ++packetId;// OK_PACKET
}
if (isCanClose2Client) {
session.releaseConnectionIfSafe(conn, LOGGER.isDebugEnabled(), false);
endRunning();
}
ok.serverStatus = source.isAutocommit() ? 2 : 1;
recycleResources();
if (isCanClose2Client) {
source.setLastInsertId(ok.insertId);
ok.write(source);
}
this.affectedRows = ok.affectedRows;
}
}
/**
* select
*
* 行结束标志返回时触发将EOF标志写入缓冲区最后调用source.write(buffer)将缓冲区放入前端连接的写缓冲队列中等待NIOSocketWR将其发送给应用
*/
@Override
public void rowEofResponse(byte[] eof, BackendConnection conn) {
this.netOutBytes += eof.length;
ServerConnection source = session.getSource();
conn.recordSql(source.getHost(), source.getSchema(), node.getStatement());
// 判断是调用存储过程的话不能在这里释放链接
if (!rrs.isCallStatement()||(rrs.isCallStatement()&&rrs.getProcedure().isResultSimpleValue()))
{
session.releaseConnectionIfSafe(conn, LOGGER.isDebugEnabled(), false);
endRunning();
}
eof[3] = ++packetId;
buffer = source.writeToBuffer(eof, allocBuffer());
int resultSize = source.getWriteQueue().size()*MycatServer.getInstance().getConfig().getSystem().getBufferPoolPageSize();
resultSize=resultSize+buffer.position();
source.write(buffer);
//TODO: add by zhuam
//查询结果派发
QueryResult queryResult = new QueryResult(session.getSource().getUser(),
rrs.getSqlType(), rrs.getStatement(), affectedRows, netInBytes, netOutBytes, startTime, System.currentTimeMillis(),resultSize);
QueryResultDispatcher.dispatchQuery( queryResult );
}
/**
* lazy create ByteBuffer only when needed
*
* @return
*/
private ByteBuffer allocBuffer() {
if (buffer == null) {
buffer = session.getSource().allocate();
}
return buffer;
}
/**
* select
*
* 元数据返回时触发将header和元数据内容依次写入缓冲区中
*/
@Override
public void fieldEofResponse(byte[] header, List<byte[]> fields,
byte[] eof, BackendConnection conn) {
this.netOutBytes += header.length;
for (int i = 0, len = fields.size(); i < len; ++i) {
byte[] field = fields.get(i);
this.netOutBytes += field.length;
}
header[3] = ++packetId;
ServerConnection source = session.getSource();
buffer = source.writeToBuffer(header, allocBuffer());
for (int i = 0, len = fields.size(); i < len; ++i) {
byte[] field = fields.get(i);
field[3] = ++packetId;
// 保存field信息
FieldPacket fieldPk = new FieldPacket();
fieldPk.read(field);
fieldPackets.add(fieldPk);
buffer = source.writeToBuffer(field, buffer);
}
fieldCount = fieldPackets.size();
eof[3] = ++packetId;
buffer = source.writeToBuffer(eof, buffer);
if (isDefaultNodeShowTable) {
for (String name : shardingTablesSet) {
RowDataPacket row = new RowDataPacket(1);
row.add(StringUtil.encode(name.toLowerCase(), source.getCharset()));
row.packetId = ++packetId;
buffer = row.write(buffer, source, true);
}
} else if (isDefaultNodeShowFullTable) {
for (String name : shardingTablesSet) {
RowDataPacket row = new RowDataPacket(1);
row.add(StringUtil.encode(name.toLowerCase(), source.getCharset()));
row.add(StringUtil.encode("BASE TABLE", source.getCharset()));
row.packetId = ++packetId;
buffer = row.write(buffer, source, true);
}
}
}
/**
* select
*
* 行数据返回时触发将行数据写入缓冲区中
*/
@Override
public void rowResponse(byte[] row, BackendConnection conn) {
this.netOutBytes += row.length;
this.selectRows++;
if (isDefaultNodeShowTable || isDefaultNodeShowFullTable) {
RowDataPacket rowDataPacket = new RowDataPacket(1);
rowDataPacket.read(row);
String table = StringUtil.decode(rowDataPacket.fieldValues.get(0), conn.getCharset());
if (shardingTablesSet.contains(table.toUpperCase())) {
return;
}
}
row[3] = ++packetId;
if ( prepared ) {
RowDataPacket rowDataPk = new RowDataPacket(fieldCount);
rowDataPk.read(row);
BinaryRowDataPacket binRowDataPk = new BinaryRowDataPacket();
binRowDataPk.read(fieldPackets, rowDataPk);
binRowDataPk.packetId = rowDataPk.packetId;
// binRowDataPk.write(session.getSource());
/*
* [fix bug] : 这里不能直接将包写到前端连接,
* 因为在fieldEofResponse()方法结束后buffer还没写出,
* 所以这里应该将包数据顺序写入buffer(如果buffer满了就写出),然后再将buffer写出
*/
buffer = binRowDataPk.write(buffer, session.getSource(), true);
} else {
buffer = session.getSource().writeToBuffer(row, allocBuffer());
//session.getSource().write(row);
}
}
@Override
public void writeQueueAvailable() {
}
@Override
public void connectionClose(BackendConnection conn, String reason) {
ErrorPacket err = new ErrorPacket();
err.packetId = ++packetId;
err.errno = ErrorCode.ER_ERROR_ON_CLOSE;
err.message = StringUtil.encode(reason, session.getSource()
.getCharset());
this.backConnectionErr(err, conn);
}
public void clearResources() {
}
@Override
public void requestDataResponse(byte[] data, BackendConnection conn) {
LoadDataUtil.requestFileDataResponse(data, conn);
}
public boolean isPrepared() {
return prepared;
}
public void setPrepared(boolean prepared) {
this.prepared = prepared;
}
@Override
public String toString() {
return "SingleNodeHandler [node=" + node + ", packetId=" + packetId + "]";
}
}

Some files were not shown because too many files have changed in this diff Show More